Cannot read property 'address_1' of undefined - React - javascript

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

Related

OBSOLETE: React: setState for primitive value doesn't work with ===

EDIT: Obsolete, I made some mistake in another piece of code and the received data had the wrong data type.
I have a variable that stores the index of a selected item. I used a conditional expression based on that variable to add/remove a class name so said item is rendered in a distinguishing way. I boiled down the problem to this snippet:
function App() {
const [selectedItem, setSelectedItem] = setState(-1);
setSelectedItem(0);
console.log(selectedItem);
return (
<>
{selectedItem !== 0 && <p>no item selected</p>}
{selectedItem === 0 && <p>item {selectedItem} selected</p>}
</>
);
}
This snippet always displays that no item is selected.
The hook is called useState, not setState.
Calling setSelectedItem inside the render function will trigger an infinite loop. You need to move it to an effect hook so it only runs one.
export default function App() {
const [selectedItem, setSelectedItem] = useState(-1);
useEffect(() => {
setSelectedItem(0);
}, []);
console.log(selectedItem);
return (
<>
{selectedItem !== 0 && <p>no item selected</p>}
{selectedItem === 0 && <p>item {selectedItem} selected</p>}
</>
);
}
What is setState ? Do you mean useState ? And also, you shouldn't update the state like this, you should do it in a useEffect, and use an empty array as dependency, so it will run only once, when your component is being mounted:
useEffect(() => {
setSelectedItem(0);
},[])
Then it is working like a charm
Replace === and !== with == and !=, respectively, and voila, it works. Alas, a warning is reported that one shall use === instead.
selectedItem is an array, apparently primitives make bad references. Still, it is bizarre to some extent that inside <p> the variable is unboxed automatically, while for evaluating === it isn't and thus an array is compared to a primitive, which is not true, no matter the values. == compares every shit in JS that you feed it with, so here it works.
Hope, this saves somebody 2 hours of debugging.
If somebody has a correct workaround for this, please share below.

JS only execute if statement based on condition

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

Conditional rendering in React if an array is empty

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.

React single line component

Hello I have a component which doesnt return anything. Im following a tutorial and the person is using newer syntax which confuses me a bit. The component looks like this:
const Alert = ({alerts}) => alerts !== null && alerts.length > 0 && alerts.map(alert => (<div key={alert.id} className={`alert-${alert.type}`}>{alert.msg}</div>));
I simply want to know how to write this without it being single line. So i can see what's going on. Much appreciated in advance. For as far as i am aware you always need to return something.
const Alert = ({ alerts }) => {
if (alerts !== null && alerts.length > 0) {
return alerts.map(alert => (
<div key={alert.id} className={`alert-${alert.type}`}>
{alert.msg}
</div>
));
}
return null
};
Things at play here are:
Arrow Functions
Array.Map
JSX
Template Literals
Basically its a component that takes in an alerts property (Array) as a prop (<Alert alerts={[...]} />). It checks whether the passed array is present and is not empty and then maps over it. For every item in the array, we are rendering a div containing the alert message.
Hope this helps!
Very roughly (i.e., untested):
const Alert = ({alerts}) => {
if ((alerts === null) || (alerts.length === 0)) {
return null
}
return alerts.map(alert => (
<div
key={alert.id}
className={`alert-${alert.type}`}
>
{alert.msg}
</div>
))
}
const Alert = ({alerts}) => {
if (!alerts || !alerts.length) return null
return (
<>
{alerts.map(alert => (
<div key={alert.id} className={`alert-${alert.type}`}>{alert.msg}</div>
)}
</>
)
}
I think what you are struggling with is generally the one-liner syntax, which doesn't need a return if there are no braces present.
What I mean is that this line
return alerts.map(alert => {
return (<div key={alert.id} className={`alert-${alert.type}`}>{alert.msg} </div>)
})
Would be the same as this line
return alerts.map(alert => (<div key={alert.id} className={`alert-${alert.type}`}>{alert.msg} </div>))

Targeting nested object properties inside an parent object

I'm trying to change the state of opening/closing times (and the post to an endpoint) on multiple days which are return to me in an object like so:
I have rendered these in a React component:
{this.state.time.map(each => (
<Fragment key={each.code}>
<OpeningHours
code={each.code}
day={each.description}
name={each.code}
open={each.open !== null ? each.open : '00:00'}
close={each.close !== null ? each.close : '00:00'}
onChange={this.onTimeChange}
/>
</Fragment>
))}
How would I iterate through the array of objects until I find the index of either the opening or closing time of this day? So far I've tried this, which works, but only if there was, say, just an opening time or one field in general. The issue arises since I have 2 fields to edit:
onTimeChange(e) {
let inputs = this.state.inputs.slice();
for(let i in inputs){
if(inputs[i].name == event.target.name){
inputs[i].value = event.target.value;
this.setState ({inputs});
break;
}
}
}
const newInputs = inputs.map(p =>
p.name === <unique_name/code>
? { ...p, [e.target.name]: e.target.value }
: p
);
this.setState({inputs:newInputs,});
Change the onTimeChange() method to get the unique_name:onTimeChange(e, code)
A simple example: https://codesandbox.io/s/v1y14rl2oy
Since index is unique, you can do
inputs[index] = { ...inputs[index], [e.target.name]: e.target.value }
too without using map() as in the sandbox.

Categories