I'm writing a react redux based web application, and I am not able to map out the response which I get from the redux store. I always get an error saying map is not a function. I am able to view the data via the console by logging it but it crashes the next second i use a map function to retrieve them. This is the response and I'm not able to map any of the data.
This is the code I created to get the blockcount response.
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { fetchPost } from '../../Redux/Post-Redux/PostActionMethods';
import './PostBody.css';
function PostBody({postdata}) {
console.log(postdata);
var blockcount = postdata.postchapter.map((key)=>{
return key.blockcount
})
console.log(blockcount);
return postdata.isLoading ? (<h2>Loading...</h2>) :
postdata.error ? (<h2>{postdata.error}</h2>) :
(
<div>
<p>Some texts</p>
</div>
)
}
const mapStatetoProps = (state) =>{
return{
postdata:state.PostChapter
}
}
export default connect(mapStatetoProps)(PostBody);
When i run this i get the error: TypeError: postdata.postchapter.map is not a function
How do i rectify this and obtain the content response while im not able to retrieve the blockcount itself??
This is the content response i need to retrieve correctly.
map() is method of an Array, but postdata.postchapter is an Object.
Can't you just use
var blockcount = postdata.postchapter.blockCount;
instead of
var blockcount = postdata.postchapter.map((key)=>{
return key.blockcount
})
-- Edit --
If you need to render postdata.postchapter.content;
text property for example,
then:
function PostBody({postdata}) {
console.log(postdata);
var content = postdata.postchapter.content?.map((item, index)=>{
return <p key={index}>{item.text}</p>
})
return postdata.isLoading ? (<h2>Loading...</h2>) :
postdata.error ? (<h2>{postdata.error}</h2>) :
(
<div>
{content}
</div>
)
}
Here is react documentation about rendering lists
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;
I have the code below in pages/github and when I go to localhost:3000/github the code executes as expected. I get JSON data.
function GithubAPI(props) {
// Render posts...
return (<div>{props.data.name}</div>)
}
// This function gets called at build time
export async function getStaticProps() {
// Call an external API endpoint to get posts
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const data= await res.json()
console.log(data)
return {
props: {
data,
},
}
}
export default GithubAPI
When I import this component into another component I get problems.
pages/about
import GithubAPI from './github'
function About(props) {
console.log(props)
return (
<div>
<div>About</div>
<GithubAPI/> {/* TypeError: Cannot read property 'name' of undefined */}
</div>
)
}
export default About
I do not know how the developers of Next.js expect us to structure our code so that we can make these kinds of API calls and still export our components for import into other components. How am I expected to do this?
You cannot run getStaticProps/getServerSideProps in any non-page components. One must prop pass instead.
I try to pass an array of object from localhost:5000/users to Table component as a prop but I can't.
I can fetch data from localhost:5000/users and when I try to do console.log inside it, I can see data. But when I try to do console.log outside fetch function, it returns an empty array.
The question is how can I pass the data to Table component if the data is not visible outside the fetch function ?
import React from 'react';
import './App.css';
import Table from './Table';
function App() {
let obj = [];
fetch('http://localhost:5000/users')
.then((response) => {
return response.json();
})
.then((data) => {
return obj = data;
})
.then(() => {
console.log(obj); // Here it returns correct data from localhost:5000/users
return obj;
});
console.log(obj); // But right here, it returns an empty array
return (
<div>
<Table data={obj} /> {/* The question is how can I pass data from localhost:5000/users to Table component ? */}
</div>
)
}
export default App;
You need to use state and useEffect state in React.js .
I would recommend to invest more time on useState and useEffect. To do so React.js official documentation is good source to study. Here is also some resource links: how to use useState
how to use useEffect
import React, {useState} from 'react';
import './App.css';
import Table from './Table';
function App() {
const [obj, setObj] = useState([])
useEffect(() => {
fetch("http://localhost:5000/users")
.then((response) => {
return response.json();
})
.then((data) => {
//return obj = data;
setObj(data); // setting obj using setObj
})
.then(() => {
console.log(obj); // Here it returns correct data from localhost:5000/users
return obj;
});
}, []);
console.log(obj); // But right here, it returns an empty array
return (
{/* The question is how can I pass data from localhost:5000/users to Table component ? */}
)
}
export default App;
A solution can be : Create a state inside a constructor in your class.
Now when you fetch, setState the data inside your state :)
Now if you create a function outside your fetch it can be like this
onClick = () => {
console.log(this.state.data)
}
Now, you can do what you want with your data on all your component :)
And if you want to use the same component for many data, your state need to be an array, and you need to map your state :)
Have fun
I think this is happening because the fetch API call is a promise, therefore, the second console.log console.log(obj); // But right here, it returns an empty array runs before the promise resolves.
You can use state and useEffect as mentioned by Rahul Amin. I have created a js fiddle you can checkout. here. https://jsfiddle.net/titbetegya/owk7eg2a/18/
I have a problem with a react child component, using redux and react-router.
I'm getting a value from the URL through this.props.match.params to make an API call on componentDidMount. I'm using this.props.match.params as I want to use data from the API in my content if someone navigates to this particular URL directly.
When I navigate to the the component by clicking a the component renders fine. The API is called via an action and the reducer dispatches the data to the relevant prop.
When I navigate to the component directly by hitting the URL, the API is called (I can see it called in the Network section of devtools), but the data isn't dispatched to the relevant prop and I don't see the content I expect.
I was hoping that by using this.props.match.params to pass a value to the API call I could hit the component directly and get the data from the API response rendered how I want.
Below is my component code. I suspect something is wrong with my if statement... however it works when I navigate to the component by clicking a Link within my React App.
What Am I missing?
import React from 'react';
import { connect } from 'react-redux';
import { fetchData } from '../../actions';
class Component extends React.Component {
componentDidMount() {
this.props.fetchData(this.props.match.params.id);
}
renderContent() {
if(!this.props.content) {
return (
<article>
<p>Content loading</p>
</article>
)
} else {
return (
<article>
<h2>{this.props.content.title}</h2>
<p>{this.props.content.body}</p>
</article>
)
}
}
render() {
return (
<>
<div className='centered'>
{this.renderContent()}
</div>
</>
)
}
}
const mapStateToProps = (state) => {
return { post: state.content };
}
export default connect(mapStateToProps, { fetchData: fetchData })(Component);
Try updating renderContent to this:
renderContent = () => {
if(!this.props.content) {
return (
<article>
<p>Content loading</p>
</article>
)
} else {
return (
<article>
<h2>{this.props.content.title}</h2>
<p>{this.props.content.body}</p>
</article>
)
}
}
It looks like you forgot to bind this to renderContent
I fixed the problem, huge thanks for your help!
It turned out the API request was returning an array, and my action was dispatching this array to props.content.
This particular API call will always return an array with just one value (according to the documentation it's designed to return a single entry... but for some reason returns it in an array!). I was expecting an object so I was treating it as such.
I converted the array into an object in my reducer and now it's behaving as it should.
So overall turned out to be a problem with my data structure.
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).