props.stories.map is not a function? - reactjs

Hi I'm trying to make a simple news app to learn react. Keep getting an error which I don't understand. Why app worked fine with a json placeholder api. However now it's displaying the error props.stories.map is not a function.
Here is my code.
import NewsList from './components/NewsList';
class App extends React.Component {
constructor() {
super()
this.state = {
stories: [],
}
}
componentDidMount() {
fetch(`https://newsapi.org/v2/top-headlines?country=gb&apiKey=${API_KEY}`)
.then(res => res.json()
.then(data => this.setState({stories: data}))
)
}
render(){
const { stories } = this.state
return (
<div className="App">
<NewsList stories={stories} />
</div>
);
}
}
import Story from './Story'
import styled from 'styled-components'
const NewsList = props => {
return(
<NewsListWrapper>
{
props.stories.map(story => (
<Story story={story} />
)
)
}
</NewsListWrapper>
)
}
import React from 'react';
import styled from 'styled-components';
export const Story = props => (
<StoryWrapper>
<h2>{props.story.title}</h2>
<p>{props.story.author}</p>
<p>{props.story.articles}</p>
</StoryWrapper>
)
I have console.log the api response and the data was being received fine. It's simply when I use this other api response.
And I've hidden my api_key just for this post, so it's not issues with that.
Thanks for any advice.

In New API documentation (https://newsapi.org/docs/endpoints/top-headlines) you can see that your request return object of form
{
status: string
totalResults: number
articles: Array
}
So you need to set to stories not data but data.articles:
fetch(`https://newsapi.org/v2/top-headlines?country=gb&apiKey=${API_KEY}`)
.then(res => res.json()
.then(data => this.setState({stories: data.articles}))
)

Try this,
import NewsList from './components/NewsList';
class App extends React.Component {
constructor() {
super()
this.state = {
stories: [],
storiesData:[]
}
}
componentDidMount() {
fetch(`https://newsapi.org/v2/top-headlines?country=gb&apiKey=${API_KEY}`)
.then(res => res.json()
.then(data => this.setState({stories: data}))
)
}
render(){
const { stories } = this.state
return (
<div className="App">
<NewsList stories={stories} />
</div>
);
}
}
import Story from './Story'
import styled from 'styled-components'
const NewsList = props => {
console.log(props.stories);
this.setState({storiesData:props.stories});
return(
<NewsListWrapper>
{
this.state.storiesData.map(story => (
<Story story={story} />
)
)
}
</NewsListWrapper>
)
}
import React from 'react';
import styled from 'styled-components';
export const Story = props => (
<StoryWrapper>
<h2>{props.story.title}</h2>
<p>{props.story.author}</p>
<p>{props.story.articles}</p>
</StoryWrapper>
)

Related

confused by state/props and updating UI in nextjs app

My nextjs app uses getInitialProps to fetch data from a streaming API, and then checks every second thereafter for updates. When the API changes (when the current track ends and a new one begins) I would like for the trackName to change on screen. What I have here works so long as I log the output to console, but for some reason the UI will not update.
I believe that trackName is passed down to the child as a prop? And then the child is trying to set the state in componentDidMount? My understanding of this feels shaky.
This is my _app.js:
import React, { useState } from 'react'
import Player from '../components/Player'
function MyApp({ Component, pageProps, tracks }) {
const url = "https://kchungradio.out.airtime.pro/kchungradio_a"
const trackName = tracks.current.name
return (
<>
<Player
url={url}
trackName={trackName}
/>
<Component {...pageProps} />
</>
)
}
MyApp.getInitialProps = async (appContext) => {
let res = await fetch("https://kchungradio.airtime.pro/api/live-info-v2")
let data = await res.json()
return { tracks: data.tracks }
}
export default MyApp
And this is my Player:
import { Component } from 'react'
import React from 'react'
import ReactPlayer from 'react-player'
export default class Player extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
setInterval(() => {
fetch("https://kchungradio.airtime.pro/api/live-info-v2", {
method: "GET"
})
.then(response => {
if (!response.ok) {
return Promise.reject(response)
}
return response.json()
})
.then(data => {
this.updateCurrentTrack(data.tracks.current.name)
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
console.log(jsonError)
}).catch(genericError => {
console.log("Generic error from API")
console.log(error.statusText)
})
} else {
console.log("Fetch error")
console.log(error)
}
})
}, 1000)
}
updateCurrentTrack = (name) => {
this.setState({
trackName: name
})
console.log(name)
}
render() {
const { url, trackName } = this.props
return (
<div class="player">
<ReactPlayer
url={url}
/>
<div>{trackName}</div>
</div>
)
}
}
this.props.trackName is not the same than this.state.trackName. You are setting a state element that is never declared in the contructor and using the trackName froms this.props which is only received once from it's parent component.
try this in the constructor:
constructor(props) {
super(props)
this.state = {
trackName: '',
}
}
and in your jsx
render() {
const { url, trackName } = this.props
const { trackName: stateTrackName } = this.state // rename since trackName has already been declared
return (
<div class="player">
<ReactPlayer
url={url}
/>
<div>{stateTrackName || trackName}</div>
// or whats the same
// <div>{this.state.trackName || this.props.trackName}</div>
</div>
)
}
My idea is that please use useState and useEffect.
In my next.js applications:
I import useEffect and useState from react.
Create 2 constact in useState.
Create a useEffect that and in the array of useEffect, set first const.
In the useEffect, change amount of second const.
This code is a dashboard page of one of my apps.
// IMPORT CTRL
import DashboardCtrl from "../../components/dashboard/ctrl";
// IMPORT DETAILS COMPONENTS
import UserInfo from "../../components/dashboard/userInfo";
import NewProjects from "../../components/dashboard/newProjects";
import { useState,useEffect } from "react";
const Dashboard = () => {
const [content,setcontent]=useState("UserInfo");
const [details,setdetails]=useState(<UserInfo/>);
useEffect(()=>{
if (content=="UserInfo") {
setdetails(<UserInfo/>);
}else if(content=="NewProjects"){
setdetails(<NewProjects/>);
}
},[content])
return (
<main>
<div className=" container mx-auto flex justify-between items-start my-8">
<div className=" w-1/4">
{details}
</div>
<div className=" w-1/4">
<DashboardCtrl setcontent={setcontent}/>
</div>
</div>
</main>
);
}
export default Dashboard;

React API data not showing up in JSX

[enter link description here][1]I am fetching API in react. I am able to see data in console but it is not appearing in JSX. I want to see Data id, name and value. But it is not appearing in browser.
[1]: https://codesandbox.io/s/late-thunder-456qp?file=/src/App.js
import React from 'react';
import axios from 'axios'
import './App.css';
class Main extends React.Component {
constructor(props) {
super(props)
this.state = {
users: [],
error: ''
}
}
componentDidMount(){
axios.get('https://jsonplaceholder.typicode.com/users')
.then( response => {
console.log(response);
this.setState({users: response.data})
})
.catch(error =>{
console.log(error);
})
}
render() {
const { users } = this.state
return (
<div>
<h2> Main Page</h2>
<p class="para-text"> Data from API</p>
{
users.length ?
users.map(post => <div key ={ users.id }> { users.name} </div>) : null
}
</div>
);
}
}
export default Main;
when mapping you named the key to your map as post and therefore when displaying them in jsx you must refer to that key
attached is a forked version of your sandbox https://codesandbox.io/s/late-thunder-456qp?file=/src/App.js
import "./styles.css";
import React from "react";
import axios from "axios";
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [],
error: ""
};
}
componentDidMount() {
axios
.get("https://jsonplaceholder.typicode.com/users")
.then((response) => {
this.setState({ users: response.data });
})
.catch((error) => {
console.log(error);
});
}
render() {
const { users } = this.state;
return (
<div>
<h2> Main Page</h2>
<p class="para-text"> Data from API</p>
{users.length > 0
? users.map((post) => <div key={post.id}> {post.name} </div>)
: null}
</div>
);
}
}
export default Main;

How can I put the data inside the MuiDataTable with these codes?

This is my code and how can I push the data inside the MuiDataTable of data={data} with my componentDidMount(). I can already fetch the data from my user collection, however, I do not know enough about mui datatable on how to pass it there. This is the one mui datatable -
https://www.material-ui-datatables.com/
import React, {Component} from 'react';
import MUIDataTable from "mui-datatables";
import {firestore} from './../../../firebase/firebase.utils';
const columns = ["Display Name","Email"];
class UserTable extends Component {
state = { user : null}
componentDidMount() {
firestore.collection('users')
.get()
.then( snapshot => {
const users = []
snapshot.forEach(doc => {
const data = doc.data()
users.push(data)
})
this.setState({ users : users})
// console.log(snapshot)
})
.catch(error => console.log(error))
}
render() {
return (
<MUIDataTable
title={"Users"}
columns={columns}
data={data}
// options={options}
/>
);
}
}
export default UserTable;
You need to pass array of object formatted with your column names' as key like {"Display Name": value, Email:other value}.
You need to pass the state into the constructor like:
constructor() {
super();
this.state = { users: [] };
}
And then you just have to pass this.state.usersto MUIDataTable props data, like:
<MUIDataTable
title={"Users"}
columns={this.columns}
data={this.state.users}
// options={options}
/>
For the example I used axios ro fetch data but this work with firestore to:
import MUIDataTable from "mui-datatables";
import { Component } from "react";
import * as axios from "axios";
class UserTable extends Component {
columns = ["Display Name", "Email"];
constructor() {
super();
this.state = { users: [] };
}
componentDidMount() {
axios
.get("/users.json")
.then((res) => {
const userData = res.data.map((u) => {
return {
"Display Name": u.name,
Email: u.email
};
});
console.log(userData);
this.setState({
users: userData
});
})
.catch((error) => console.log(error));
}
render() {
return this.state.users ? (
<MUIDataTable
title={"Users"}
columns={this.columns}
data={this.state.users}
// options={options}
/>
) : (
<div>Loading...</div>
);
}
}
export default UserTable;
With your code this would like :
import MUIDataTable from "mui-datatables";
import { Component } from "react";
import * as axios from "axios";
class UserTable extends Component {
columns = ["Display Name", "Email"];
constructor() {
super();
this.state = { users: [] };
}
componentDidMount() {
firestore.collection('users')
.get()
.then( snapshot => {
const users = []
snapshot.forEach(doc => {
const data = doc.data()
users.push({"Display Name":data.displayName, Email: data.email});
});
return users;})
.then(userList => this.setState({users:userList});
})
.catch(error => console.log(error))
}
render() {
return this.state.users ? (
<MUIDataTable
title={"Users"}
columns={this.columns}
data={this.state.users}
// options={options}
/>
) : (
<div>Loading...</div>
);
}
}
export default UserTable;
UPDATE:
here you can find a working example with firestore

How to call API using react js

i am new in react. Can anyone help me that How to call API using react js and show the response in page. I am trying the following code but getting error.
export default class UserList extends React.Component {
componentDidMount() {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
const users = res.data;
this.setState({users});
})
}
render() {
return (
<ul>
{this.state.users.map(user => <li>{user.name}</li>)}
</ul>
)
}
}
Hi you missed to declare the state. you need to declare it in UserList Component. Please follow this example:
import React from 'react';
import axios from 'axios';
export default class UserList extends React.Component {
state = {
users: []
};
componentDidMount() {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
const users = res.data;
this.setState({users});
})
}
render() {
return (
<ul>
{this.state.users.map(user => <li>{user.name}</li>)}
</ul>
)
}
}

Getting undefined when trying to fetch data from an api - React

So im trying to set a variable with the data im getting from the API.
when im console logging it into my browser everything works fine but when im trying to set my variable on React the variable ending up undifeind.
can someone tell me what am i missing here?
this is my code :
import React from 'react'
let news
function getNews () {
fetch(
'https://newsapi.org/v2/top-headlines?country=us&apiKey=6f9cf5e6b9684bd3a6a8117e35feb1c9'
)
.then(res => res.json())
.then(data => {
news = data
return news
})
}
getNews()
class NewsApi extends React.Component {
render () {
return <div />
}
}
export default NewsApi
Your getNews function is async. You should use state to save your data. So, as soon as the data fetched, you can use the data in your component.
import React from 'react';
class NewsApi extends React.Component {
constructor(props) {
super(props);
this.state = {
news: []
};
this.getNews = this.getNews.bind(this);
}
componentDidMount() {
this.getNews()
}
getNews() {
fetch('https://newsapi.org/v2/top-headlines?country=us&apiKey=6f9cf5e6b9684bd3a6a8117e35feb1c9')
.then(res => res.json())
.then((data) => {
this.setState({news:data.articles});
});
}
render() {
console.log(this.state.news)
return (
<div></div>
);
}
}
export default NewsApi;
try this : It outputs what you want.
** Notes:
Fetch is Async functions, means, it has to be called inside (for example) life cycle method like componentDidMount.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
news: []
};
}
componentDidMount() {
fetch(
"https://newsapi.org/v2/top-headlines?country=us&apiKey=6f9cf5e6b9684bd3a6a8117e35feb1c9"
)
.then(response => response.json())
.then(data => this.setState({ news: data.articles }));
}
render() {
console.log(this.state.news);
return <div />;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here is a live example (You can see console output also)\
Please look at the snippet below for an example implementation.
Some key points:
Try to do your data fetching in componentDidMount()
Use state and this.setState()
This will cause an automatic rerender after the data is fetched.
Handy for working with asynchronous function calls: these tools make it easy to use data in components without knowing when it will become available and help eliminate the undefined issue you were having.
class NewsApi extends React.Component {
state = {
articles: []
};
componentDidMount() {
fetch(
"https://newsapi.org/v2/top-headlines?country=us&apiKey=6f9cf5e6b9684bd3a6a8117e35feb1c9"
)
.then(res => res.json())
.then(data => data.articles)
.then(articles => {
this.setState({ articles });
});
}
render() {
return (
<div>
<h1>Articles</h1>
<ArticleList articles={this.state.articles} />
</div>
);
}
}
const ArticleList = props => (
<div>
<ol>
{props.articles.map((article, index) => (
<div key={index}>
<li>{article.title}</li>
<br />
</div>
))}
</ol>
</div>
);
function App() {
const appStyle = {
fontFamily: "sans-serif",
textAlign: "center"
};
return (
<div className="App" style={appStyle}>
<NewsApi />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Resources