I'm working on a react/redux project that uses axios to get data, but some of the data is not accessible using dot notation for some reason.
For example, this pulls in the data just fine:
console.log(this.props.apts.data)
while this returns TypeError: Cannot read property 'apartments' of undefined:
console.log(this.props.apts.data.apartments)
I know for a fact there is data present. This happens in other parts of the file as well, not just this one particular array of objects.
Here's one of the JSON files I'm working with: https://api.myjson.com/bins/x2ad4
This is my action creator:
export const fetchPosts = () => {
return async dispatch => {
const response = await jsonPlaceholder.get('https://api.myjson.com/bins/x2ad4');
dispatch({ type: 'FETCH_POSTS', payload: response })
}
}
If I change the payload to response.data.apartments, I can then get access to the apartments array in my component like so:
class PostList extends Component {
componentDidMount() {
this.props.fetchPosts();
}
formatPricing(price) {
if(this.props.apts[0].pricing.currency === 'EUR') {
return `${price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".")} €`;
} else {
return `£${price}`
}
}
renderList() {
return this.props.apts.map(apartment => {
return (
<Grid key={apartment._id} item lg={4} md={6} xs={12}>
<div>
<img style={{objectFit: 'cover'}} src={apartment.images.photos[0].path} alt="" width='100%' height="150px"/>
<div className="containeraps">
<div className='aptprice'>{this.formatPricing(apartment.pricing.price)}</div>
<div className="monthutil">
<div>Per Month</div>
<div>Utilities incl.</div>
</div>
</div>
<div className="movein">
from 29.24.2019 - {parseInt(apartment.details.squareMeters)} m² - {apartment.bedroomCount} bedroom
</div>
</div>
</Grid>
)
})
}
render() {
return (
<div className='aptlist'>
<Grid container spacing={24}>
{this.renderList()}
</Grid>
</div>
);
}
}
const mapStateToProps = (state) => {
return { apts: state.PostsReducer }
}
export default connect(mapStateToProps,
{fetchPosts}
)(PostList);
But if I just use the response as the payload, then I can't access the apartments array because 'return this.props.apts.data.apartments.map(apartment =>' gives me an TypeError: Cannot read property 'apartments' of undefined.
Unfortunately, I need data from just the response as well, so I don't have a workaround.
This is my reducer:
export default (state = [], action) => {
switch(action.type) {
case 'FETCH_POSTS':
return action.payload;
default:
return state;
}
}
I initially thought it had something to do with not loading the data, but since some of the data is present it must all be accessible. That's what's stumping me right now, why is only some of the data accessible while some isn't.
I've switched around how I'm referencing the data using dot notation in various different files to change how I pull it in, but so far there's only 1 way to get access to the apartments array. The crazy thing is that I can console.log it even when it doesn't work, and I see the data in the console.
Update: I can't update anything that is more than 2 calls deep using dot notation. So props.apts.queryParams returns info, but using dot notation to get anything inside of queryParams doesn't work either.
And for the apartments array, I cannot view any unique indexes of that array. So props.apts.apartments display the array of data, while props.apts.apartments[0] doesn't display any data at all. I tried using spread operators to fix this but I wasn't able to find a solution.
I found a workaround, it's not elegant but it gets the job done. I created several reducers and dispatched different levels of the json based on the data I needed.
dispatch({ type: 'APARTMENT_LIST', payload: response.data.apartments })
dispatch({ type: 'APARTMENT_TOTAL', payload: response.data})
dispatch({ type: 'CITY_NAME', payload: response.data.queryParams})
Related
I'm modifying some code to use React Query rather than useEffect - see new code using React Query below:
import axios from 'axios';
import { useQuery } from '#tanstack/react-query'
function MembersList() {
const { data } = useQuery(["members"], () => {
return axios.get('http://localhost:3001/members').then((res) => res.data)
})
return (
<div className="List">
{data?.map((value, key) => {
return (
<div className="member">
<div key={member_id}> {value.forename} {value.surname}</div>
</div>
);
})}
</div>
);
}
export default MembersList;
I'm getting an error that 'member_id' is not defined - arising from the row where I try and add 'member_id' as a key (see below).
Error Message
'Member_id' is the first field in the array, see below JSON from Insomnia:
JSON showing the 'member_id field'
The error is clearly telling me to define 'member_id' but I'm not sure how or where specifically to do that.
If I remove the 'key={member_id}' then the code compiles and runs, but throws a warning that "Each child in a list should have a unique "key" prop.".
I've reviwed many similar issues on Stack Exchange and React docs, but still can't see why my code isn't working.
The thing you are getting back from the request is an object. An object can be thought of like a dictionary, you look up a word and it has a definition attached to it. member_id is just one of the words you can look up in this object. Right now, you don't specify what member_id is so javascript "thinks" it should be a variable that you defined, similar to data above. However, what you really want is the value of member_id that is present in the object. Therefore you should change it to value.member_id where value is one of the objects in your data list.
A visual way of thinking about it is like this
data = [{...}, {...}, ...];
value = data[0]; // this is what map is doing except for 0...N-1 where N is the length of your list
value;
> {...}
value.member_id;
> 1
Therefore, change your code to this:
import axios from 'axios';
import { useQuery } from '#tanstack/react-query'
function MembersList() {
const { data } = useQuery(["members"], () => {
return axios.get('http://localhost:3001/members').then((res) => res.data)
})
return (
<div className="List">
{data?.map((value, key) => {
return (
<div className="member" key={value.member_id}> // <<<
<div> {value.forename} {value.surname}</div>
</div>
);
})}
</div>
);
}
export default MembersList;
import React, {useState, useEffect} from "react";
import './App.css';
function App() {
const [ data, setData ] = useState([]);
useEffect( ()=> {
loadData();
//getData();
}, []);
const loadData = async () => {
await fetch("https://randomuser.me/api")
.then(response => response.json())
.then(receiveddata => setData(receiveddata));
}
console.log(data);
return (
<div className="App">
<p> Fetch/ Async/ Await</p>
{data.map(user => (
<div key={user.id}>{user.name}, {user.email}</div>
))}
</div>
);
}
export default App;
I'm getting this error on the console,
Uncaught TypeError: data.map is not a function
You are getting the error because data is not an Array. The response that you are getting back is an object that has two properties results and info. Since your state is an array, set the state to receiveddata.results instead.
Firsteval, your console.log(data) will return nothing, because the state will not be updated when your code will run the console.log() ; so don't expect to get debug info with that.
Next, your response is an object, not an array.
You can do this way : setData(receiveddata.results) then you can map() your data.
In your render you should also check if map is not empty like this:
{data && data.map(user => (
<div key={user.id}>{user.name}, {user.email}</div>
))}
You need to check the structure of data comping from your API. Your array is inside "results" property. And "title" is an object as well. You can find a working example of your code over here CodeSandbox. Check the console you will better understand the structure of your response
You're getting this error because you are treating data as an array while it contains an object, which is what the API returns in this case.
Consider the schema of the data you're getting back from the API which is something similar to below. You can see that results is the key for an array within an object:
{
results: [
{
...
email: string;
id: {
name: string;
value: string;
}
gender: string;
name: {
title: string;
first: string;
last: string;
}
...
}
}
So, to access the first, last names and email properties, you need to call the map() method on the results array within the object which was assigned to data as shown below.
Make sure that data.results is assigned before trying to access it with data.results && data.results.map(..):
{
data.results && data.results.map(user => (
<div key={user.id.value}>
{user.name.first}, {user.name.last}, {user.email}
</div>))
}
I have a project where I used redux-saga to make an API call with axios and return the data to the store, which then I mapStateToProps using redux and now I want to map() it and show it on my DOM, but I'm getting "undefined".
Two things keep happening:
either the data doesn't get called in time and the render happens to fast so it says its undefined.
i get map() is not a function or for {blog.id} -- id is undefined.
When I console.log(blogs) i see my array of blogs so I'm not sure what I'm doing wrong. Is it because blogs is an array and so I need to do some kind of for loop to go through the each item and then map it?
Here is my main chunk of code and the console log
import React, {Component} from 'react';
import {connect} from 'react-redux'
import {loadBlogs} from '../../store/actions/blogActions'
class Bloglist extends Component {
componentDidMount() {
this.props.loadBlogs();
}
render() {
const {blogs} = this.props
console.log(blogs)
return (
<div>
<h1>{blogs.map(blog => (
<span>{blog.id}</span>
))}</h1>
</div>
)
}
}
const mapStateToProps = blogs => ({
blogs
})
const mapDispatchToProps = dispatch => ({
loadBlogs: () => dispatch(loadBlogs()),
})
export default connect(mapStateToProps, mapDispatchToProps)(Bloglist)
here is a console log example and an error:
Uncaught TypeError: blogs.map is not a function
this is when I just comment out the map() lines and just return a "hello", this console.log is showing me that the data is coming back
Bloglist.js:14 (3) [{…}, {…}, {…}]
Please let me know if you want any more samples or info. It's really important to get this done so any help would be appreciated! Thanks!
There can be 2 issues.
Please cross check the type of blogs, it should be array as map method works only on array.
You have to check for array's length before mapping it. As map method doesn't works on empty array.
Try this code --
<div>
<h1>{blogs && blogs.length > 0 ? blogs.map(blog => (
<span>{blog.id}</span>
)) : null}</h1>
</div>
At the beginning, your blogs is not an Array.
You should to update your reducer initialState, set blocks to be an empty array as default, just like
in reducer.js
const initialState = {
blogs: [],
};
export default (state = initialState, action) => {
switch(action) {
case....
default:
return { ...state };
}
}
Or, you also should check the blogs before rendering.
Array.isArray(blogs) && blogs.map(item => (
<div>
{// item details goes here}
</div>
))
use ? to handle this error. mostly probably the error is coming from the saga. you have to provide the code better suggest a solution.
<div>
<h1>{blogs?.map(blog => (
<span>{blog.id}</span>
))}</h1>
</div>
Try like this.
class Example extends Component {
state:{}
render(){
//.....code
}
}
Hello dear Stack Overflow, I just started a Gatsby website but I'm having issues looping through an array passed to a component.
What I'm trying to do:
I have a Gatsby page called blog.js, in this page I have been showing blog titles retrived via GraphQL. Using a loop directly in the blog.js page I can see all the titles.
My loop inside of blog.js looks like this
<div>
<h1>Blogg data</h1>
{data.posts.edges.map (({ node }) => (
<p>{node.title}</p>
))}
</div>
It retrieves data from the following GraphQL query
export const query = graphql`
query BlogPageQuery {
posts: allSanityPost(
limit: 12
sort: { fields: [publishedAt], order: DESC }
) {
edges {
node {
id
publishedAt
mainImage {
asset {
_id
}
alt
}
title
_rawExcerpt
slug {
current
}
}
}
}
}
`
Instead of creating the blog posts previews in blog.js I instead want to use a component to do this. I've created a component called BlogPostPreviewGrid and call it like this from blog.js
<BlogPostPreviewGrid blogPosts={data}/>
My BlogPostPreviewGrid component currently looks like this
const BlogPostPreviewGrid = blogPosts => {
return (
<div>
<p>Here be component data</p>
{console.log(blogPosts)}
{blogPosts.posts.edges.map (({ node }) => (
<p>{node.title}</p>
))}
</div>
)
}
export default BlogPostPreviewGrid
What's not working:
I cannot loop through the data retrieved by the component, when running the loop I get a console error massage stating blogPostPreviewGrid.js:13 Uncaught TypeError: Cannot read property 'edges' of undefined
What have I tried:
My first response was to console.log blogPosts, console.log shows the array object, I've attached a the array from Chromes console log
blogPostPreviewGrid.js:13 {blogPosts: {…}}blogPosts: posts: edges: Array(2)0: node: {id: "54fe241a-c7d4-50d2-be51-4403304ddc86", publishedAt: "2020-01-05T23:00:00.000Z", mainImage: {…}, title: "Testpost2", _rawExcerpt: Array(1), …}__proto__: Object1: {node: {…}}length:
I've also written a conditional statement so that the component only tries to render the data if something exists in blogPosts and tried tweak the loop. I ended up with doing a git reset --hard so the conditional rendering is not present right now.
Thank you for all replies!
Values passed to children components in react are passed in one big object known as props: https://reactjs.org/docs/components-and-props.html.
So you need to either destructure props in your function call:
const BlogPostPreviewGrid = ({blogPosts}) => {
// do stuff
console.log(blogPosts)
}
Or use props object
const BlogPostPreviewGrid = props => {
// do stuff using blogPosts
console.log(props.blogPosts);
}
If your component looked something like this <Foo prop1={prop1} prop2={prop2} /> then you would access it like so:
const Foo = props => {
console.log(props.prop1);
console.log(props.prop2);
}
or like so:
const Foo = ({ prop1, prop2 }) => {
console.log(prop1);
console.log(prop2);
}
This should work:
BlogPostPreviewGrid.jsx
const BlogPostPreviewGrid = ({blogPosts}) => {
return (
<div>
{blogPosts.posts.edges.map (({ node }) => (
<p>{node.title}</p>
))}
</div>
)
}
export default BlogPostPreviewGrid
Trying to orient through the dark depths of Redux-React-API jungle - managed to fetch data from API and console.log it - but neither me nor my Google skills have managed to find out why it doesn't render.
React Components
Parent Component:
class Instagram extends Component {
componentWillMount(){
this.props.fetchInfo();
}
render() {
return (
<div className="container">
<div className="wrapper">
<InstagramPost />
</div>
</div>
)
}
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ fetchInfo }, dispatch);
}
export default connect(null, mapDispatchToProps)(Instagram);
Child Component:
class InstagramPost extends Component {
render() {
console.log(this.props.info);
this.props.info.map((p,i) => {
console.log("PROPS ID: " + p.id);
})
return (
<div>
<h1>POSTS</h1>
<ul className="uls">
{
this.props.info.map((inf, i) =>
<li key={i}>{inf.id}</li>
)
}
</ul>
</div>
)
}
}
const mapStateToProps = ({ info }) => {
return { info }
}
export default connect(mapStateToProps)(InstagramPost);
Redux Action method:
const ROOT_URL = 'https://jsonplaceholder.typicode.com/posts';
export const fetchInfo = () => {
const request = axios.get(ROOT_URL);
return {
type: types.FETCH_INFO,
payload: request
};
}
Redux Reducer method:
export default function(state = [], action) {
switch (action.type) {
case FETCH_INFO:
return action.payload.data;
default:
return state;
}
}
The JSON file looks like this:
In the console - it works and I get my Objects:
The state is also updated:
But when I map over this.props.info, trying to render this.props.info.id, nothing is rendered on the page.. Incredibly thankful for any input!
Looks like your props aren't set on the initial render. I'm guessing your API call hasn't finished.
Try checking the the variable is set or is an array first:
Something like this:
class InstagramPost extends Component {
render() {
if(!this.props.info) return null
return (
<div>
<h1>POSTS</h1>
<ul className="uls">
{
this.props.info.map((inf, i) => {
return <li key={i}>{inf.id}</li>
})
}
</ul>
</div>
)
}
}
const mapStateToProps = ({ info }) => {
return { info }
}
export default connect(mapStateToProps)(InstagramPost);
Or you may want to check the length this.props.info.length > 0.
There were two problems. As Mayank Shukla pointed out, nothing was returned from the map callback because of the block braces ({}) without a return statement.
The other problem was in the reducer. As the redux state for info is an array of users, you need to replace the old state on FETCH_INFO rather than add the fetched array to the beginning of it. Otherwise, you're maintaining an array of arrays of users, which will grow by one on each fetch.
Note that you don't need any checks on this.props.info, as it will be initialized to [] by your reducer and [].map(f) == [].
For redux debugging I can very much recommend installing the Redux DevTools extension, as it will allow you to inspect all updates to the store. It needs a little setup per project, but that's well worth it.
Oh, and in the future, you might want to refrain from updating your question with suggestions from the comments/answers, as the question will no longer make sense to other visitors :-)