I'm building a cinema listings project. There is a block with information about each film and then the film times below it.
I have 2 dropdown menus - one to select a film, one to select a date. I'm using ternary operators to render the results but can't get it so that if a film doesn't have any showings on a particular day, the block of information about the film is hidden when that date is selected.
I'll just post an example for one of the films.
Here's the json file that the listing information is taken from -
[
{
"id": "film1",
"filmTitle": "Knives Out",
"paragraphText": "Lorem ipsum dolor sit amet",
"mon": ["12:00", "15:00", "19:00"],
"tue": ["13:10", "16:30", "19:00", "21:00"]
}
]
Here's part of the js file with one of the film listings -
constructor(props){
super(props);
this.state = {
filmListings: [],
selectedFilm: "allFilms",
selectedDate: "allDates"
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event){
const {name, value} = event.target
this.setState({
[name]: value
});
}
componentDidMount() {
const FilmListings = require("./components/booking/filmTimesData.json");
this.setState({ filmListings: FilmListings })
}
render() {
const filmsArray = require("./components/booking/filmTimesData.json");
const selectedFilm = this.state.selectedFilm
const selectedDate = this.state.selectedDate
return (
<form id="searchForm">
<div id="filmDateContainer">
<div className="searchOption">
<h2>Film:</h2>
<img src={FilmSearch} alt="film icon"/>
<select
name="selectedFilm"
value={this.state.selectedFilm}
onChange={this.handleChange}
className="dropdown"
>
<option value="allFilms">All Films</option>
<option value="film1">Knives Out</option>
<option value="film2">Judy and Punch</option>
<option value="film3">Harriet</option>
</select>
</div>
<h2 id="or">OR</h2>
<div className="searchOption">
<h2>Date:</h2>
<img src={DateSearch} alt="date icon"/>
<select
name="selectedDate"
value={this.state.selectedDate}
onChange={this.handleChange}
className="dropdown"
>
<option value="mon">Monday 2nd December</option>
<option value="tue">Tuesday 3rd December</option>
<option value="wed">Wednesday 4th December</option>
</select>
</div>
</div>
<div>
{(selectedFilm === "film1" || selectedFilm === "allFilms") ?
<FilmInfo filmTitle={filmsArray[0].filmTitle} paragraphText={filmsArray[0].paragraphText}/> : " "}
{(selectedDate === "mon" || selectedDate === "allDates")
&& (selectedFilm === "film1" || selectedFilm === "allFilms") ?
<Mon day={filmsArray[0]}/> : " "
}
{(selectedDate === "tue" || selectedDate === "allDates")
&& (selectedFilm === "film1" || selectedFilm === "allFilms") ?
<Tue day={filmsArray[0]}/> : " "
}
{(selectedDate === "wed" || selectedDate === "allDates")
&& (selectedFilm === "film1" || selectedFilm === "allFilms") ?
<Wed day={filmsArray[0]}/> : " "
}
</div>
</form>
);
}
}
In this example, there's no showing of the film on a Wednesday so how can I get the info block for the film not to show when Wednesday is selected from the dropdown list?
I think that trying to limit it to one film item as opposed to looping over multiple ones has unfortunately made things more complicated, not less. It seems like you're working backwards from the conclusion that, "some films have a Wed showing, therefore we need to conditionally render a Wed component". In other words, starting with the difference between the data points rather than what they have in common.
We could write some convoluted conditional that checks whether a particular property exists, but it will be very brittle and you'll probably end up throwing it out anyway once you move on to mapping everything. It makes more sense to just be agnostic about which specific properties each film object has and allow the data to flow through your component more naturally.
Starting with your JSON file, group the showings data into a set of more discrete properties. You'll now be able to easily access and loop over the showings rather than trying to access each one individually by name.
[
{
"id": "film1",
"filmTitle": "Knives Out",
"paragraphText": "Lorem ipsum dolor sit amet",
"showings": [
{"date": "mon", "times": ["12:00", "15:00", "19:00"]},
{"date": "tue", "times": ["13:10", "16:30", "19:00", "21:00"]}
]
}
]
On to the component. Let's start by getting rid of the unnecessary lifecycle method, and some render variables. Since your data is coming from a static, local file, you can just import it at the top of the component and include it directly in the constructor. It only makes sense to use componentDidMount when processing data that is not immediately accessible on mount (e.g. it is coming from a remote resource, or waiting on some other component in the tree). I'm using import syntax here as it should be available to you in a boilerplate React environment.
import filmData from "./components/booking/filmTimesData.json";
class FilmListings extends React.Component {
constructor(props){
super(props);
this.state = {
filmListings: filmData,
selectedFilm: "allFilms",
selectedDate: "allDates"
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event){
const {name, value} = event.target
this.setState({
[name]: value
});
}
render() {
const { filmListings, selectedFilm, selectedDate } = this.state;
return (
...
);
}
}
Now the render function. I'm going to leave the filmDateContainer alone because it's essentially fine, although of course you'll want to map over the options from the filmListings instead of hardcoding, which should become clearer after this next bit.
We're going to replace the entire div after filmDateContainer with this mapping function, which first loops over each film object to make sure it does not conflict with selectedFilm. It then loops over that film object's showings to make sure each one in turn does not conflit with selectedDate. Essentially, we've created a staggered filter which nests the data by order of importance (crucially, as you may now notice, by the same order that it is structured in the JSON file).
{filmListings.map(film =>
selectedFilm === "allFilms" || selectedFilm === film.id ? (
<div key={film.id}>
<FilmInfo filmTitle={film.filmTitle} paragraphText={film.paragraphText}/>
{film.showings.map(showing =>
selectedDate === "allDates" || selectedDate === showing.date ? (
<Day showing={showing} key={film.id + showing.date}/>
) : null)}
</div>
) : null)}
Remember to assign appropriate, unique keys each time you render a mapping function so that React can keep track of everything. The nesting is also already quite dense so you might want to think about separating each map into its own variable which returns that little snippet of JSX it's responsible for, especially if you have any extra conditions to consider or properties to map.
If you have any questions about any of this drop a comment and I'll try my best to explain.
Edit:
I think the best way to handle the more complex conditional logic you are after is to write some selectors. This is a concept you'll get more familiar with when you move on to more complicated state management, but it can be used just as effectively here by adding some simple functions before your render return. As a bonus, it's also going to clean up the render return which is quite nice because it was already starting to look a bit ugly. By combining selectors you can start to reason about your state in a more logical, readable way, and define common operations which produce specific outcomes.
I also got rid of the ternaries and went for pure && evaluation since it's a bit cleaner.
render() {
const { filmListings, selectedFilm, selectedDate } = this.state;
const allFilms = selectedFilm === 'allFilms';
const allDates = selectedDate === 'allDates';
const validDate = date => allDates || selectedDate === date;
const filmHasDate = showings => showings.some(showing => validDate(showing.date));
const validFilm = (id, showings) => (allFilms || selectedFilm === id) && filmHasDate(showings);
return (
<form>
...
{filmListings.map(film => validFilm(film.id, film.showings) && (
<div key={film.id}>
<FilmInfo filmTitle={film.filmTitle} paragraphText={film.paragraphText}/>
{film.showings.map(showing => validDate(showing.date) && (
<Day showing={showing} key={film.id + showing.date}/>
))}
</div>
))}
</form>
);
}
One of the drawbacks to this approach is that you are doing all these calculations every time the component rerenders. Depending on the kind of application you have, how often the component rerenders, and how large the input array is, that could limit performance, but in the vast majority of cases its not going to be an issue.
The alternative would be to do these calculations once, only when handleChange is called, and store the result in a new state variable (e.g. filteredListings) which you could then map over directly in your render function. However, then you've got duplicate state on your hands with the same data in multiple places, which can be a headache to reason about and synchronise when your data set gets to be any considerable size. Just something to think about!
In your example, you could simply do:
{((selectedDate === "wed" && filmsArray[0][selectedDate]) || selectedDate === "allDates")
&& (selectedFilm === "film1" || selectedFilm === "allFilms")
&&
<Wed day={filmsArray[0]}/>
}
It would check that your film object actually has a "wed" key and conditionally render the Wed component.
Note that I ditched the ? because if the result of
((selectedDate === "wed" && filmsArray[0][selectedDate]) || selectedDate === "allDates")
&& (selectedFilm === "film1" || selectedFilm === "allFilms")
is false, the component would not be rendered. I just find it cleaner.
You could do this for each of your cases.
Your code would then look like:
<div>
{(selectedFilm === "film1" || selectedFilm === "allFilms") &&
<FilmInfo filmTitle={filmsArray[0].filmTitle} paragraphText={filmsArray[0].paragraphText}/>
}
{((selectedDate === "mon" && filmsArray[0][selectedDate]) || selectedDate === "allDates")
&& (selectedFilm === "film1" || selectedFilm === "allFilms")
&&
<Mon day={filmsArray[0]}/>
}
{((selectedDate === "tue" && filmsArray[0][selectedDate]) || selectedDate === "allDates")
&& (selectedFilm === "film1" || selectedFilm === "allFilms")
&&
<Tue day={filmsArray[0]}/>
}
{((selectedDate === "wed" && filmsArray[0][selectedDate]) || selectedDate === "allDates")
&& (selectedFilm === "film1" || selectedFilm === "allFilms")
&&
<Wed day={filmsArray[0]}/>
}
</div>
But I do think that you should re-architecture your solution to have a simpler and more robust way to display the desired component. Something in the line of #lawrencee-witt suggestion.
The final solution would be heavily influenced by the control you have over that JSON file.
I can create a small CodePen example if you want more info.
Related
I'm looking to display some html in my React/Next.js web app based on conditional logic. I got the basics working but having issues showing the same html if multiple variable conditions are true. For example, the following code works fine.
{category === 'ford' &&
<div>Car</div>
}
{category === 'harley' &&
<div>Motorcycle</div>
}
I'm having issues showing multiple variables as true. The following code doesn't work but show the logic I'm after.
{category === 'ford' || category === 'toyota' &&
<div>Car</div>
}
//this code doesn't work.
I realise a simple answer is to separate operators for each separate condition, however i'm trying to avoid duplicating the html <div>Car</div> (as in my actual application contains large forms in this section).
You will need to wrap the OR-Condition in parentheses like so:
(category === 'ford' || category === 'toyota') && <div>Car</div>
you can also make use of Array includes method
I would make an array for e.g.
const cars = ["honda", "toyota", ....];
const motorcycle = ["euler", "ducati", ...];
{cars.includes(category) ? <div> cars </div> : <div> motorcycles </div> }
const isCarCategory = ["ford", "toyota"].includes(category);
const isMotorcycleCategory = ["harley"].includes(category);
return (
<div>
{isCarCategory && <div>Car</div>}
{isMotorcycleCategory && <div>Motorcycle</div>}
</div>
);
Just wrap your condition inside parenthesis.
Parenthesis must be used, if multiple conditions needs to be checked.
Check this link about Precedence And Associativity https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
{(category === 'ford' || category === 'toyota') &&
<div>Car</div>
}
you can wrap it all in a different function and use a switch statement (or arrays) to handle not managed category
like this
const renderVehicle = (category) => {
switch(category) {
case 'honda':
case 'ford':
return <div>Car</div>
case 'harley':
return <div>Motorcycle</div>
default:
return <div>Not found</div>
}
}
const renderVehicle2 = (category) => {
const cars = ['honda', 'ford']
const motorcycles = ['harley']
if(cars.includes(category)){
return <div>Car</div>
}
if(motorcycles.includes(category)){
return <div>Motorcycle</div>
}
return <div>Not found</div>
}
the simple answer is to wrap the condition in ()
{(category === 'ford' || category === 'toyota') &&
<div>Car</div>
}
I would like to explain my problem of the day.
I have an element, and inside I have 2 conditions on props
when i reuse the component i call the props socialSupervisor or socialOperator.
it works perfectly.
on the other hand I find it long to read, so I am looking for a way of refactoring to have 0 props or 1 only.
I am open to any proposal thank you very much.
<p>
{socialSupervisor &&
(!isLoading &&
lastMessage?.type === "text" &&
lastMessage?.author?.type === "supervisor" &&
lastMessage?.author?._id === authUser._id ? (
<span>
Moi:
</span>
) : lastMessage?.author?.type === "operator" ? (
<span>
conseiller: {lastMessage?.author?.name}:
</span>
) : lastMessage?.author?.type === "supervisor" ? (
<span>
superviseur: {lastMessage?.author?.name}:
</span>
) : (
""
))}
{socialOperator &&
(!isLoading &&
lastMessage?.type === "text" &&
lastMessage?.author?.type === "operator" ? (
<span>
Moi:
</span>
) : lastMessage?.author?.type === "supervisor" ? (
<span>
superviseur: {lastMessage?.author?.name}:
</span>
) : (
""
))}
</p>
An improvement may be:
function getLabel(type) {
switch (type) {
case "operator":
return "conseiller";
case "supervisor":
return "superviseur";
case "me":
return 'Moi';
}
}
function renderSocialSupervisor() {
const isMe = lastMessage?.author?._id === authUser._id;
const label = getLabel(isMe ? 'me' : lastMessage?.author?.type);
return isLoading || lastMessage?.type !== "text" ? null : (
<span>{label}: {lastMessage?.author?.name}</span>
);
}
function renderSocialOperator() {
const isMe = lastMessage?.author?._id === authUser._id;
const label = getLabel(
lastMessage?.author?.type === "operator"
? "me"
: lastMessage?.author?.type
);
return isLoading || lastMessage?.type !== "text" ? null : (
<span>
{label}: {lastMessage?.author?.name}
</span>
);
}
return (
<p>
{socialSupervisor && renderSocialSupervisor()}
{socialOperator && renderSocialOperator()}
</p>
);
Better would be to write separate components, and to pass the right props to them. The less logic you put inside a single component return statement the better.
De-structure nested props/variables:
lastMessage to {author, type}
Or even refactor names of author and instead of _id and type to author_id and author_type, so you can de-structure easily:
{{author_id, author_type}, type}
I'll start by asking you what the types of socialSupervisor and socialOperator props? I can guess they're booleans (true or false), so if one of them is true, does it mean the other prop will be false?
If the answer is yes, you can use the ternary operator to render the component with one prop (you can decide which one to leave).
{ socialSupervisor ?
<span>Social supervisor render</span> :
<span>Social operator render</span> }
Secondly, I found that you re-use your spans, they share the same classes and only vary on their content.
If you ask me, I would create a function that returns the name of the author. Then, I would create a function that returns a span with the content I need.
const getAuthorLabel = (type, name) => {
switch (type) {
'me': return 'Moi: ';
'supervisor': return `superviseur: ${name}`;
'operator': return `conseiller: ${name}`;
default: return '';
}
}
const getAuthLabel = ({ author, type }) => {
const isMe = author?._id === authUser?._id;
const labelType = isMe && type === 'text' ? 'me' : author?.type;
const label = getAuthorLabel(labelType, author?.name);
return <span className="text-xs font-semibold mr-1">{label}</span>;
}
And last but not least, do you need these props? I mean, the last message would still be the last message, with all of its props, including the message type, author type, author name, author id, etc.
So maybe you could render the label directly in the paragraph element, without the need to check for their values. Of course, this is irrelevant if you need these props for something else, or they mean something else by themselves.
A whole refactor for the component, as I can imagine it would be something similar to this:
const YourComponent = ({ lastMessage }) => {
// your other logic goes here
return <p>{renderAuthLabel(lastMessage)}</p>;
}
I am fetching data from an api and I need to render a component based on an if statement and I cant seem to figure it out. A customer has an array of roles. Customer.items is an array of customer objects. This is the if statement I am trying but doesnt work:
{customers?.items?.length > 1 && !roles.includes("Super") && (...component
Basically I need to check if roles array has "Super" and customers.items only has one element then dont render the component.
Also if roles is "Super" and customer.items.length > 1 then still render the component
customers.items: [{id: 2, name: "G"}, {id: 3, name: "H"}]
roles: ["Super", "Admin"]
This will render the component in all cases except when customers.items has only one element and if the roles include 'Super'.
const hasSingleCustomer = customers?.items?.length === 1
const hasSuperRole = roles.includes('Super'))
{!(hasSingleCustomer && hasSuperRole) && <Component />}
You can also write it as {(!hasSingleCustomer || !hasSuperRole) && <Component />} if you prefer.
You can try this approach
{(customers.items.length > 1 && roles.includes("Super")) ? <If Success Component/> : <Failure Component>}
I have written as per your request, as I am checking if the roles array has "Super" in it, You can still manipulate the operation inside the brackets(), and we have to use ? and : to make sure the conditions work,
Happy Coding :)
My suggestion is to split the equation/ conditions into smaller variables and then use them to create a validity condition. This way, your code is more readable and easier to maintain
const length = customers.items.length
const isSuperUser = roles.includes('Super')
const isAdminUser = roles.includes('Admin')
const isAllowedForSuper = isSuperUser && length === 1
const isAllowedForAdmin = isAdminUser && length === 0
if (isAllowedForSuper || isAllowedForAdmin) {
return <Component {...props} />
}
return null
I having this condition on a form group:
if((age>17 && (this.frType=="Infant"))
|| (age>40 && this.frType=="Grandchild")
|| (age<=5 &&
(this.frType!="Child"
|| this.frType!="Infant"
|| this.frType!="Grandchild" || this.frType!="Cousin")))
It contain 3 main conditions:
If a person aged 17, cannot be set to infant
If a person is bigger than 40, he cannot be a grandchild
If a person is less than 5 years, he should be child, infant, grandchild or cousin.
If one of these conditions is true, I will send an error message.
The error I am receiving is:
[ts] This condition will always return 'true' since the types
'"Child"' and '"Infant"' have no overlap. [2367]
On this part of the if condition`:
|| this.frType!="Infant" || this.frType!="Grandchild" || this.frType!="Cousin")))
I am using the exact condition in a different component, and it does not show an error.
if((age>17 && (this.family_relation_type=="Infant"))
|| (age>40 && this.family_relation_type=="Grandchild")
|| (age<=5 &&
(this.family_relation_type!="Child" ||
this.family_relation_type!="Infant" ||
this.family_relation_type!="Grandchild" ||
this.family_relation_type!="Cousin")))
Here is how I am calculating the age in both components:
let timeDiff = Math.abs(Date.now() - this.formGroup.controls['dob'].value);
let age = Math.floor((timeDiff / (1000 * 3600 * 24))/365);
Consider the standalone expression:
(this.frType!="Child" || this.frType!="Infant")
If frType is Child, the second part will be true, so the expression will evaluate to true. If frType is Infant, then the first part will be true, so the expression will evaluate to true. If frType is neither Child nor Infant, then the first part will be true, and the expression will, again, evalute to true - the logic is faulty, it'll always resolve to true.
(If you add additional || conditions for Grandchild and Cousin, the same thing keeps happening - it'll always resolve to true)
Either use && instead:
|| (age<=5 && (
this.frType!="Child"
&& this.frType!="Infant"
&& this.frType!="Grandchild"
&& this.frType!="Cousin"
))
Or, to make the logic easier to follow, you might consider using an array, and use .includes:
const kidsFiveAndUnder = ['Child', 'Infant', 'Grandchild', 'Cousin'];
// ...
|| (age <= 5 && !kidsFiveAndUnder.includes(this.frType))
Maybe i can help someone with this.
In my case the error was triggered by:
*ngIf="fooArray.length === 0"
so i modified it to be:
*ngIf="fooArray.length < 1"
Makes no sense to me, but it works.
I struggled with this problem recently. Sharing my experience here
Basically IDE does not allow to compare an object.enum with a string. As a solution, a method in the component.ts is added to compare the enum
Details :
export enum Status {
NEW,
PROGRESS,
FINISHED
}
export interface Model {
id : number;
name : string;
status : Status
}
Now in the component.html, I was trying to compare the model status
<div *ngFor="let m of modelItems" >
<i *ngIf="m.status === 'NEW'" class="icon-new"></i>
</div>
Error : This condition will always return 'false' since the types 'Status' and 'string' have no overlap.ngtsc(2367)
I also tried defining the status enum in the component.ts and used that for comparison
public StatusEnum = Status;
<div *ngFor="let m of modelItems" >
<i *ngIf="StatusEnum[m.status] === 'NEW'"
class="icon-new"></i>
</div>
With the above solution, there is no IDE error, but the condition never true, as the enum[value] give a numeric value.
The next option I tried was as follows
<div *ngFor="let m of modelItems" >
<i *ngIf="m.status=== StatusEnum[StatusEnum.NEW]" class="icon-new"></i>
</div>
But ended up with the error again in the IDE
Error : This condition will always return 'false' since the types 'Status' and 'string' have no overlap.ngtsc(2367)
Finally what solved the issue it implement a method in the component.ts
Solution
component.ts
public StatusEnum = Status; //To refer in the HTML
checkStatus(m: Model, status: Status): boolean {
return Status[m.status] as unknown === status;
}
Note : Status[m.status] as unknown
HTML
<div *ngFor="let m of modelItems" >
<i *ngIf="checkStatus(m,StatusEnum.NEW)"
class="icon-new"></i>
</div>
Define the data types of all your variables explicitly.
For example, this code has the same error mentioned in the thread title and I fixed by defining the data types of the variables explicitly.
From:
const selectedLangCulture = "en"; // or "ar-SA"
const direction = "rtl";
const languageChanged =
(direction === "rtl" && selectedLangCulture === "en") ||
(direction === "ltr" && selectedLangCulture === "ar-SA");
To:
const selectedLangCulture: string = "en"; // Put the datatype string.
const direction: string = "rtl"; // Put the datatype string.
const languageChanged =
(direction === "rtl" && selectedLangCulture === "en") ||
(direction === "ltr" && selectedLangCulture === "ar-SA");
In my case, I was using a type named type for the button element with React.ComponentPropsWithRef<'button'>
type ButtonProps = {
type?: 'submit' | 'button' | 'link'; // ❌
} & React.ComponentPropsWithRef<'button'>;
the type was overridden because React.ComponentPropsWithRef<'button'> had a type in it also. I replaced it with elementType and the problem is solved.
type ButtonProps = {
elementType?: 'submit' | 'button' | 'link'; // ✅
} & React.ComponentPropsWithRef<'button'>;
In my case, I simply had to rebuild my app because the type definitions got briefly out of sync.
I have a code like this
class EventDetails extends React.Component {
constructor( props ) {
super (props);
this.state={
startdate: props.startdate || "",
enddate: props.enddate || "",
venue: props.venue || ""
}
}
componentDidMount() {
fetch(`https://www.eventbriteapi.com/v3/venues/${this.state.venue}/?token=EventBrite'sTOKEN`)
.then(response => response.json())
.then(eventvenue => this.setState({ venue: eventvenue }))
}
render() {
const { startdate, enddate, venue } = this.state;
const getDateWhenTheEventStart = new Date(Date.parse(startdate));
const theDateWhenTheEventStart = getDateWhenTheEventStart.toDateString();
const theHourWhenTheEventStart = getDateWhenTheEventStart.getHours();
const theMinuteWhenTheEventStart = getDateWhenTheEventStart.getMinutes();
const getDateWhenTheEventEnd = new Date(Date.parse(enddate));
const theDateWhenTheEventEnd = getDateWhenTheEventEnd.toDateString();
const theHourWhenTheEventEnd = getDateWhenTheEventEnd.getHours();
const theMinuteWhenTheEventEnd = getDateWhenTheEventEnd.getMinutes();
function checkTime(time) {
if (time < 10) {
time = '0' + time
}
return time;
}
return(
<React.Fragment>
<Container text>
<Header as="h1">Date and Time</Header>
<p><strong>Starts:</strong> {theDateWhenTheEventStart} | {checkTime(theHourWhenTheEventStart)}:{checkTime(theMinuteWhenTheEventStart)}</p>
<p><strong>Ends:</strong> {theDateWhenTheEventEnd} | {checkTime(theHourWhenTheEventEnd)}:{checkTime(theMinuteWhenTheEventEnd)}</p>
<Header as="h1">Location</Header>
<List>
<List.Item>{venue.name}</List.Item>
{venue.address.address_1 != undefined && <List.Item>{venue.address.address_1}</List.Item>}
{venue.address.localized_area_display != undefined && <List.Item>{venue.address.localized_area_display}</List.Item>}
</List>
</Container>
</React.Fragment>
)
}
}
export default EventDetails;
The problem of the code is here
{venue.address.address_1 != undefined && <List.Item>{venue.address.address_1}</List.Item>} // gives me the error of 'Cannot read property 'address_1' of undefined'
I suspect that this problem occured because the component render first and the program breaks because the state is not updated yet.
Do you have any ideas how to solve this problem? Thank you
Make sure that the venue.address is not null before using venue.address.address_1
{venue.address && venue.address.address_1 !== undefined && <List.Item>{venue.address.address_1}</List.Item>}
You would have to do full error checking elsewhere - for example when you set the state.
But if you would like to do a one liner and do a full Error Check, following would be possible
<List>
<List.item>Date and Time</List.item>
{venue ? venue.address ? venue.address.address_1 ? <List.Item>{venue.address.address_1}</List.Item> : <List.Item>Loading..</List.Item> : <List.Item>Loading..</List.Item> : <List.Item>Loading..</List.Item>
</List>
However, as you can see, it's a very ugly way of doing it so do validity checking elsewhere if you want to use ternary
This is the safest full check if your object is defined or not with all properies during rendering (I think the cleanest too). It will render element if your object is fully valid.
{!!venue && !!venue.address && !!venue.address.address_1
&& <List.Item>{venue.address.address_1}</List.Item>
}
the best way of doing this is to use Object.prototype.hasOwnProperty() to check whether your object contains a particular key or not :
{venue.hasOwnProperty(address) &&
venue.address.hasOwnProperty(address_1) &&
<List.Item>{venue.address.address_1}</List.Item>}
Check something like below
{venue && venue.address && venue.address_1 ? .
{venue.address.address_1} : ''}
Here you can see, first we are checking if venue object is available then we check for the inner one and then nested object. This is the best practice instead of directly displaying the value. Hope this helps :)
It's always a good idea to check than an object and all nested objects you want to access have loaded. Please find an example of how I would check. When using the && operator, Javascript will check for the first false statement and then stop. In other words, if venue is "" (an empty string is considered false), null, or undefined, Javascript will stop checking at venue, before an error similar to the one you posted above is thrown.
If venue exists, it will then check venue.address, and so on, until finally, it will return the last statement "{venue.address.address_1}". If you try the code below, you'll see what I mean.
One last thing - please make sure the venue props are actually being passed to your component, or the two List.Item components will not render (though you will not get an error).
<List>
{venue && venue.name && <List.Item>{venue.name}</List.Item>}
{venue && venue.address && venue.address.address1 && <List.Item>{venue.address.address_1}</List.Item>}
{venue && venue.address && venue.address.localized_area_display && <List.Item>{venue.address.localized_area_display}</List.Item>}
</List>
Instead of long checks like a && a.b && a.b.c, use lodash get.
https://lodash.com/docs/4.17.10#get