I have an issue regarding react and I was hoping i could get some help. I will try my best to explain my situation and i will provide examples where needed.
The situation:
I have this component:
import React , {useState} from 'react';
import axios from 'axios';
import Ui from './UI';
function App() {
const [weather, setWeather] = useState({});
const [query, setQuery] = useState({query : ''});
const handleSubmit = (e) => {
e.preventDefault();
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${query.query}&units=metric&appid=appid`)
.then(res => {
setWeather({data: res.data})
});
};
const handleChange = (e) => {
setQuery({query:e.target.value});
};
return (
<div className="App">
<form onSubmit={handleSubmit}>
<input type="text" onChange = {handleChange}/>
<input type="submit" value = 'Search!'/>
</form>
<Ui weather ={weather}/>
</div>
);
}
export default App;
It's fetching data from the openweather API. When everything is set, I pass the Weather data to the presentational component named "Ui".
The data weather object that i pass down to the Ui has properties. One of these properties looks like 'weather.data.main'. When I try to access this in my presentational component I get an error.
TypeError: Cannot read property 'main' of undefined
But i am sure main exists. How is this possible ?
here's my presentational component :
import React , {useState} from 'react';
import axios from 'axios';
function Ui(weather) {
console.log(weather.data.main);
return (
<div className="Ui">
<h2>{}</h2>
</div>
);
}
export default Ui;
First issue
weather is a property of prop passed to Ui component so you need to either
destructure it
function Ui({ weather }) {
console.log(weather.data.main);
return (
<div className="Ui">
<h2>{weather.data.main}</h2>
</div>
);
}
Or use props.weather.data.main.
function Ui(props) {
console.log(props.weather.data.main);
return (
<div className="Ui">
<h2>{props.weather.data.main}</h2>
</div>
);
}
Second issue
TypeError: Cannot read property 'main' of undefined
Now to address the 2nd issue is that, the weather property might not be available at the time it was being passed to Ui component.
There are also two ways to fix this issue.
You can check & display a loading message/gif if the value you'd like to access (weather.data.main) is still unavailable or undefined.
(validating in the child level)
function Ui({ weather }) {
if (weather === undefined ||
weather.data === undefined ||
weather.data.main === undefined)
return <div>Loading weather data...</div>
return (
<div className="Ui">
<h2>{weather.data.main}</h2>
</div>
);
}
Or you can render Ui only when Weather data is available. (It basically depends on where in the component tree you'd like to display the Loading message/gif).
function App() {
// ... rest redacted for brevity
return (
// ... rest redacted for brevity
{weather && weather.data && <Ui weather ={weather}/>}
)
}
That oddly looking && chain instructs that App should display only when weather && weather.data is available.
Instead of having to use if statements I did in the Ui components in #1 above, && is just a short-hand.
Consider this:
import React , {useState} from 'react';
import axios from 'axios';
function Ui({ weather }) {
console.log(weather.data && weather.data.main);
return (
<div className="Ui">
<h2>{}</h2>
</div>
);
}
Note that: weather.data && this will check if weather actually has data, and then checks for the main inside that data.
You have to access weather like this
function Ui({ weather }) {
console.log(weather.data.main);
return (
<div className="Ui">
<h2>{}</h2>
</div>
);
}
Initially weather is equal to {} whiche doesn't have data.main. Hence you can do the following -
{weather.data && <Ui weather ={weather}/>}
This will render Ui only when weather.data is available (not before that).
Related
I have a react component that gets data from an API end point. For the purposes of debugging when I call the API I log the result of the API call to the console and it looks like this:
The problem I have is that when I try to render the BreadCrumbLinks Property in my component I get this error:
TypeError: Cannot read properties of undefined (reading 'map')
at BreadCrumbHeader
I am getting an "Undefined" error, but I know that the data is present because I can read and render all of the other fields (for example BreadCrumbBgImage).
Also, if I comment out the map/loop the rest of the data is displayed correctly when the page loads. Then if I uncomment it out and save the file the data for the map/loop now shows correctly.
I can only assume that the code is trying to render the contents of the loop before it has been loaded.
This is what the code for the component looks like:
import React, { useState, useEffect } from 'react';
import API from "../../API";
import { useLocation } from 'react-router-dom';
import { BreadCrumbTitleSection, SubtitleSection, Subtitle } from './breadCrumbHeaderStyle';
import { Breadcrumb } from 'react-bootstrap';
function BreadCrumbHeader() {
const location = useLocation();
const [breadCrumbData, setBreadCrumbData] = useState([]);
const getBreadCrumbData = async () => {
const breadCrumbHeaderResponse = await API.fetchBreadCrumbHeader(location.pathname);
setBreadCrumbData(breadCrumbHeaderResponse);
console.log("OUT-PUT-OF-API-CALL");
console.log(breadCrumbHeaderResponse);
console.log("END-OF-OUT");
};
useEffect(() => {
getBreadCrumbData();
}, [location.pathname]);
return (
<div>
<BreadCrumbTitleSection backgroundUrl={breadCrumbData.BreadCrumbBgImage}>
<div className="container">
<div className="row no-gutters">
<div className="col-xs-12 col-xl-preffix-1 col-xl-11">
<h1 className="h3 text-white">{breadCrumbData.BreadCrumbTitle}</h1>
<Breadcrumb>
{breadCrumbData.BreadCrumbLinks.map(breadCrumbLink => (
<Breadcrumb.Item href={breadCrumbLink.LinkUrl} key={breadCrumbLink.Id} active={breadCrumbLink.IsActive}>
{breadCrumbLink.LinkText}
</Breadcrumb.Item>
))}
</Breadcrumb>
</div>
</div>
</div>
</BreadCrumbTitleSection>
<SubtitleSection>
<Subtitle> {breadCrumbData.SubTitle}</Subtitle>
</SubtitleSection>
</div>
);
}
export default BreadCrumbHeader;
Can anyone explain what is going on here and how I can solve i?
You are trying to map data before its fetched, so its an empty array (initial value of breadCrumbData state). You should use optional chaining:
{breadCrumbData?.BreadCrumbLinks?.map(breadCrumbLink =>
You are tryng to map your array before the state change, the useEffect is called on first render, your array don't have the state in the first render, you can use something like a loading hook, like this
const [loading, setLoading] = useState(false)
useEffect(() =>{
setLoading(true)
fetchData()
},[])
const fetchData = () =>{
//my api call
setLoading(false)
}
return (
{loading ? (
// my loading message or function
): (
// my show component
)}
)
this is a just an litle example how you can do
I am trying to return a placeholder for the 'null' prop values. I tried running a ternary operator and 'if' conditional statement to check if values exist before being rendered.
The props are passed from a parent component to a child component while data is being pushed to another child through react-router-dom's 'history' method.
When I run console.log the props are being passed properly however when I try to click on an image which contains 'null' I run into the current issue.
Carousel.js
import React from 'react';
import { Carousel } from 'react-responsive-carousel';
import "react-responsive-carousel/lib/styles/carousel.min.css";
import './Carousel.css';
const ImageCarousel = props => {
const photos = props.history.location.state.resources.photos;
return (
<>
<button className="backBtn" onClick={props.history.goBack}>Click to go back</button>
<Carousel>
{
photos.map(photo => {
return (
<div>
<img className='image' src={photo.url} alt="rental-carousel" />
</div>
)
})
}
</Carousel>
<div className="address-container">
<p className="address">Address:</p>
<p className="address-info">{' '}{props.history && props.history.location.state.address}</p>
</div>
</>
)
}
export default ImageCarousel;
You need to check if it's not null before getting photos value
const photos = props.history.location.state && props.history.location.state.resources ?
props.history.location.state.resources.photos :
[];
You can try the new ? optional chaining operator:
const photos = props?.history?.location?.state?.resources?.photos;
and then check if photos is undefined before rendering
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
Check the null or undefined before assigning the prop
if (props === null ) {
......
}
const photos = props?.history?.location?.state?.resources?.photos;
I have a redux state which is working fine in that my data is in the redux store. I can console.log it out with console.log(this.props) but I can't seem to render it out. It returns this error:
TypeError: Cannot read property 'somevalue' of undefined
Ususally I would map over props with a static block of html/jsx but I need this to be different html per loop so I'm trying to insert the values directly into the markup. Can anyone point me in the right direction please?
import React, {Component} from 'react';
import { connect } from 'react-redux';
class UnitClass extends Component {
state = {
unit: {}
}
render() {
console.log('Props is:', this.props); // shows correct values
return (
<ul>
<li>{this.props.unit.somevalue.value1}</li>
<li>{this.props.unit.somevalue.value2}</li>
<li>{this.props.unit.somevalue.value3}</li>
<li>{this.props.unit.somevalue.value4.someothervalue}</li>
</ul>
);
}
}
const mapStateToProps = state => ({
unit: state.setUnit.unit,
});
export default connect(mapStateToProps)(UnitClass);
try something like this
render() {
console.log('Props is:', this.props); // shows correct values
const {unit} = this.props;
return (
<ul>
{unit.somevalue && unit.somevalue.map((value)=>{
return value.someothervalue? <li>{value.someothervalue}</li>: <li>{value}</li>})
}
</ul>
);
}
note: for conditional rendering, you can use ternary operator and if you have to deal with nested conditional rendering, then I would recommend Higher Order Components, and then probably this
I'm passing a map to all my posts variable so that all my posts can appear as a single post, but it kept bringing up the error
I have tried solving it using the Reactjs Documentation from the react website, it shows almost the same code as mine.
import PropTypes from 'prop-types';
import PostItem from './PostItem';
class PostFeed extends Component {
render() {
const { posts } = this.props;
const list = posts.map((post) => <PostItem
key={post._id}
posts={post}
/>
);
return (
{list}
);
}
}
PostFeed.propTypes = {
posts: PropTypes.array.isRequired
};
export default PostFeed;
I expect every posts to appear as a single post from the postItem component
The error means, when your PostFeed mounts for first time at that time props are not available, so the error.
You can check if data available, like,
let list = "Loading...";
if(posts && posts.length > 0){
list = posts.map((post) => <PostItem key={post._id} posts={post} /> );
}
posts is probably result of an async action and its value is not available at the time that your function is doing its job. So it should have a default value or be checked before that has a value.do this:
if(this.props.posts && Array.isArray(this.props.posts) && this.props.posts.length > 0)
//then map
I am trying to connect a component to the redux store, but am receiving:
Warning: Failed prop type: The prop 'store.subscribe' is marked as required inConnect(StoreLocation), but its value is 'undefined'.
I have been using redux with this project for awhile now without issue, but this component is erroring out for some reason and I'm clueless as to why at this point :(
The store populates a collection of stores (brick and mortar locations with addresses, phone numbers, etc used for shipping selections) within DeliverySection.js.
Then each StoreLocation.js component will allow the user to view it's info, select it, etc. It's bare bones right now as I am seeing the error even at this basic point. If I switch the export default connect()(StoreLocation) statement with export default StoreLocation it works without issue.
Any ideas?
DeliverySection.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
// Components
import Loader from '../../utils/Loader'
import StoreLocation from './StoreLocation'
// Stote
import { getAllStores } from '../../../store/actions/storeLocation'
import { REACT_APP_SITE_KEY } from '../../../shared/vars'
// CSS
import '../../../css/delivery.css'
class DeliverySection extends Component {
componentDidMount(){
this.props.getAllStores(REACT_APP_SITE_KEY);
}
render() {
const { stores, isLoading } = this.props
return (
<div>
<div className="delivery-heading">
<h2>Choose a store near you:</h2>
<button className="btn btn--red btn--heading" name="ship-to-address">Ship To An Address</button>
</div>
<div>
{isLoading ? (
<Loader />
) : (
!isLoading && !!stores ? (
stores.map((store, i) => <StoreLocation key={i} store={store} />)
) : (
<div>
There are no store locations to deliver to.<br />
Ship to an address!
</div>
)
)}
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
stores: state.storeLocation.stores,
isLoading: state.storeLocation.isLoading
}
}
export default connect(mapStateToProps, { getAllStores })(DeliverySection)
StoreLocation.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { setDelivery } from '../../../store/actions/checkout'
class StoreLocation extends Component {
render() {
const { store } = this.props
return (
<div className="store-location">
<div className="store-row">
<div className="store-col"><div className="store-title">{store.title}</div></div>
<div className="store-col">
{store.address}
{store.formatted_location &&
<div>{store.formatted_location}</div>
}
</div>
<div className="store-col">
<button className="btn select-store" onClick={() => this.props.setDelivery(store)}>Ship to this store<span className="icon-checkmark"></span></button>
</div>
</div>
<div className="store-row">
<div className="store-col">
<div className="ajax-message" data-hbs-id="postal-{id}"></div>
<input type="hidden" id={`postal-${store.id}`} value={store.postal} />
<div className="see-map"><span className="icon-location"></span>See on map</div>
</div>
<div className="store-col">{store.description}</div>
<div className="store-col"></div>
</div>
{store.phone &&
<div className="store-row">
<div className="store-col"></div>
<div className="store-col">{store.phone}</div>
<div className="store-col"></div>
</div>
}
</div>
)
}
}
export default connect(null, { setDelivery })(StoreLocation)
// export default StoreLocation
It's because you are using store as your prop name. You are overwriting the prop react-redux passes through the HOC. Since, the object you pass for store does not have a subscribe method, you get this error.
If you change the name of your prop, you'll be in good shape again.
After doing a quick Google I came across this post here.
That problem, which is similar to yours, was based on the way the store was exported. Have a look at that and see if gets you going in the right direction. I can't comment without seeing your store export code.
On a personal preference note I would use something other than 'store' as the variable for each instance in your map of stores. Since you are using Redux it could get semantically confusing whether you are referring to the Redux store or an instance of a store object.
I think it's fine that you are having StoreLocation handle the setting of delivery. I'm a big fan of breaking things down into smaller components.
Finally, just because I happen to notice it, you have a misspelling in DeliverySection. Line 8 reads //Stote. I'm guessing you meant //Store.
Apologies in advance as I think this should go under the comment section, but the code you pasted looks alright. You say disconnecting the StoreLocation component fixes things. Is there a reason you want to connect that component? You're not mapping any state to props or using dispatch in that component.
Otherwise, make sure that you're correctly initializing the store with the reducers you need and check that the modules you're using are imported properly - especially the ones you are passing to the connect function (getAllStores).