I am testing out a simple next.js react app, although an error is showing up when I try to access it at localhost:300. On line 46 of my news.js page, I am trying to test if state.articles is empty then copy props to it, although next.js is telling me .length is undefined. Does anyone know why .length is undefined?
Error is as follows; TypeError: Cannot read property 'length' of undefined
any help is apprecaited
// This is the Link API
import Link from 'next/link';
import fetch from 'isomorphic-unfetch';
//import SearchForm from './components/SearchForm';
const apiKey = 'API-KEY';
const source = 'the-irish-times';
//build the url which will be sued to get the data
const url = `https://newsapi.org/v2/top-headlines?
country=ie&category=sports&apiKey=${apiKey}`;
//getNews(url) is an async method which fetchs and
returns data or and erroe
//from WWW Api
async function getNews(url){
//try fetch and catch any errors
try{
//make async call
const res = await fetch(url);
//get json data when it arrives
const data = await res.json();
//return json data
return(data);
} catch (error){
return(error);
}
}
// the news page definied as an ES6 Class
export default class News extends React.Component{
//constructor
//receive props and initialise state properties
constructor(props){
super(props)
this.state = {
newsSource: "",
url: "",
atricles: []
}
} // end constructor
// render() method generates the page
render() {
//if state.articles is empty copy props to it
**** THIS LINE
if(this.state.articles.length == 0){
this.state.articles = this.props.articles;
}
return (
<div>
{ /* display a title based on source */}
<h3>{this.state.newsSource.split("-").join(" ")}
</h3>
<div>
{ /*iterate through artiles using array map */}
{ /* display author, publishedAT, image, desc and
content */}
{ /* for each story also a link for more */}
{this.state.articles.map((article, index) => (
<section key = {index}>
<h3>{article.title}</h3>
<p className="author">{article.author}
{article.publishedAt}</p>
<img src={article.urlToImage} alt="artile
image" className="img-article"></img>
<p>{article.description}</p>
<p>{article.content}</p>
<p><Link href="/story"> <a>Read mor</a> </Link>
</p>
<p onClick = {this.test}>click..</p>
</section>
))}
</div>
<style jsx>{`
section {
width: 50%;
border: 1px solid grey;
background-color: rgb(240, 248, 255);
padding: 1em;
margin: 1em;
}
.author {
font-style: italic;
font-size: 0.8em;
}
.img-article {
max-width: 50%;
}
`}</style>
</div>
);
}//end render
}
//get initial data on server side using an AJAX call
// this will initialise the 'props' for the news page
async function getInitialProps(response){
//build the url which will be used to get the data
const initUrl = `https://newsapi.org/v2/top-
headlines?
sources=${defaultNewsSource}&apiKey=${apiKey}`;
//get news data from tje api url
const data = await getNews(initUrl);
//if the result contains an articles array then it is
good so return articles
if(Array.isArray(data.articles)) {
return {
articles: data.articles
}
}
// otherwise it contains an error, log and redirect
to error page
else {
console.error(data)
if(response) {
response.statusCode = 400
response.end(data.message);
}
}
} // end initial props
A couple problems:
1.) Mistyped state property name
I believe you meant articles not atricles.
Should be
this.state = {
newsSource: "",
url: "",
articles: []
}
not
this.state = {
newsSource: "",
url: "",
atricles: []
}
2.) Mutating immutable React state
In React we change the state with setState(). Keep in mind this is an asynchronous action and since you're calling it in the render function it will likely not appear until the next render.
if(this.state.articles.length == 0){
this.setState({ articles: this.props.articles });
}
class Application extends React.Component {
constructor(props) {
super(props);
this.state = {
articles: []
};
}
render() {
if(this.state.articles.length == 0){
return (<div>Articles has no length</div>);
}
return (
<div>
Articles has a length.
</div>
);
}
}
// Render it
ReactDOM.render(
<Application/>,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Related
I'm working on a full-stack project using Express in back-end and react in front-end. Now the problem is with a specific component. It is supposed to fetch the results of a database query from /api/blogposts. But then it goes wrong.
here's my code:
import React from 'react';
import './Blogposts.css';
import SingleBpost from '../SingleBpost/SingleBpost.js';
class Blogposts extends React.Component {
constructor(props) {
super(props);
this.state = {
receivedPosts: []
};
}
async getBpostsFromServer() {
const response = await fetch("/api/blogposts");
let myPosts = await response.json();
this.setState({receivedPosts: myPosts});
}
componentDidMount() {
this.getBpostsFromServer();
}
render() {
console.log(this.state.receivedPosts);
return(
<div id="Blogposts">
<SingleBpost title="Test" date="18/12/2021" author="Kepos Team" body="Hello, this is a test for the blogposts!" />
</div>
);
}
}
export default Blogposts;
There is only one console.log: console.log(this.state.receivedPosts); but the console gives me two results: first it prints an array that I don't recognize, then it prints the array that I need. (here's a screenshot https://imgur.com/a/q40aVkq).
But when I try to feed the data from this.state.receivedPosts into my component like this:
<SingleBpost title={this.state.receivedPosts[0].title} (etc etc) />
It doesn't work (my screen sctually turns blank).
Does anyone have any idea as to what I'm doing wrong here? Thanks for any help!
You can use && which will protect you from that case
render() {
return (
this.state.receivedPosts && this.state.receivedPosts.length
<div id="Blogposts">
<SingleBpost title="Test" date="18/12/2021" author="Kepos Team" body="Hello, this is a test for the blogposts!" />
</div>
);
}
Or handling it via ternary operator:
render() {
return (
this.state.receivedPosts && this.state.receivedPosts.length ?
<div id="Blogposts">
<SingleBpost title="Test" date="18/12/2021" author="Kepos Team" body="Hello, this is a test for the blogposts!" />
</div> : null
);
}
The following code works:
import React, { Component } from 'react';
import './App.css';
import Menu_dropdown from "./component/menu_dropdown";
class App extends Component {
constructor() {
super()
this.state = {
races: {}
}
}
componentDidMount(){
fetch('https://www.dnd5eapi.co/api/races/')
.then(response=> response.json())
.then(races => {this.setState({ races : races})});
}
render(){
const { races } = this.state;
console.log(races['results']);
return (
<div className="App">
<header className="App-header">
<Menu_dropdown races = {races}/>
</header>
</div>
);
}
}
export default App;
console will output an array of races. However,it gives me "TypeError: Cannot read property '0' of undefined" when I change to this
console.log(races['results'][0]);
why is results not an array? You can view source json here:
https://www.dnd5eapi.co/api/races/
When state is first initialized to { races: {}}, races.results will be undefined and you can't get any elements from undefined.
Simply change the constructor to:
constructor() {
super()
this.state = {
races: {results: []}
}
}
This way the component can render an empty array while loading and the fetch results when the fetch call is resolved.
I am creating a live score website that will be returning all football live events. I am using jquery to send request then getting an object response that has fixures - this contains an array of all live events and finally result - this counts the number of events (21); I have trouble mapping this data so that I can display it on my app attached is the image of response
import React from 'react';
import jQuery from 'jquery';
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
liveEvents: '',
loading: true
};
}
settingState = data => {
this.setState({
liveEvents: data.api
});
};
componentDidMount() {
var h = this;
var settings = {
async: true,
crossDomain: true,
url: 'https://api-football-v1.p.rapidapi.com/v2/fixtures/live',
method: 'GET',
headers: {
'x-rapidapi-host': 'api-football-v1.p.rapidapi.com',
'x-rapidapi-key': 'RAPIDAPI-KEY' //Private
}
};
jQuery.ajax(settings).done(function(response) {
h.setState({
loading: false
});
h.settingState(response);
});
}
render() {
var { liveEvents, loading } = this.state;
return (
<div className="livesheet">
{loading ? 'loading ' : console.log(liveEvents)}
</div>
);
}
}
export default Home;
Please refer to what is JSX.
You need to map your data in liveEvents to ReactElement / ReactElements array:
class Home extends React.Component {
...
render() {
const { liveEvents, loading } = this.state;
const { results, fixtures } = liveEvents;
// Example for rendering all fixture ids
const fixturesArray = fixtures.map(fixture => (
<div key={fixture.fixture_id}>{fixture.fixture_id}</div>
));
return (
<div className="livesheet">{loading ? 'loading' : fixturesArray}</div>
);
}
}
export default Home;
Also, you don't really need to use jQuery with react, read how to make AJAX calls and all relevant subject in the docs.
You can put your data in state, and show them in your app (in render) like this:
<div>{this.state.mydata.map
(m=><div>m.teamname</div>)}
</div>
update
Well if you have want to extract array as well, you should write another map in first map like this:
<div>
{this.state.mydata.map(mydata=>
<div>
{mydata.map(myarray=>
<div>{myarray.teamname}</div>
)mydata.id
</div>)}
</div>
Its something like this, you probbly need to check the syntax,if you use two nested map you can extract your array in your data as well
I hope you get the idea and works for you
I have a container component which fetches weather data from https://openweathermap.org/. The container component then feeds that state data into a presentational component.
While some of the state properties exist, in the presentational component, others however are undefined. If I wrap them in a timeout however they appear.
I've been searching for why this might be but I've been unable to find any reasons why they'd be undefined after being fetched in the container component and passed into the presentational component.
CodePen: https://codepen.io/ZCKVNS/pen/wpGaMe?editors=0010
Article about presentational and container components: https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
const App = data => {
setTimeout( () => {
console.log('clouds', data.data.clouds.all); //defined
console.log('lat',data.data.coord.lat); //defined
console.log('lon', data.data.coord.lon); //defined
}, 100);
return (
<div>
{ data.data.clouds.all } <!-- Not defined -->
</div>
);
}
class AppContainer extends React.Component {
constructor() {
super();
this.state = { data: {} };
}
componentDidMount() {
fetch( 'https://api.openweathermap.org/data/2.5/weather?zip=43055,us&appid=4e08bb16c8936bd92b4780f9e2cdf00f' )
.then( res => res.json() )
.then( data => this.setState( { data } ) );
}
render() {
return React.createElement( App, { data: this.state.data } );
}
}
ReactDOM.render(
<AppContainer />,
document.getElementById( 'container' )
);
You could just return null from App until you receive the data, or, as a convention, return some kind of UI (such as a loading icon) while the fetch is completing:
In AppContainer:
this.state = { data: null }; // so ternary operator is falsey when data isn't done fetching
Then in App:
return (
<div>
{
data.data ?
data.data.clouds.all
:
<img src={LOADING_ICON_HERE} />
}
</div>
);
What this does is check if the prop data exists. If it does then it renders data.data.clouds.all. If not, it shows a loading icon while the request finishes. If you didn't want to show anything you could shorten it to:
{
data.data &&
data.data.clouds.all
}
My node.js server sends with socket.io new data each 10s. In my web application I update this.state each time that my server sends data and force to update with forceUpdate()
However, my react component doesn't refresh, I don't know why. I followed the doc but I missed something...
Parent :
class DataAnalytics extends React.Component {
constructor(props) {
super(props);
socket = this.props.socket;
this.state = {data: []};
socket.on('dataCharts', (res) => {
console.log("new data charts : "+res);
var data = JSON.parse(res);
this.setState({data: data});
this.forceUpdate();
});
}
componentWillUnmount() {
socket.off('dataCharts');
}
render() {
return (
<div id="dataAnalytics">
<Stats data={this.state.data}></Stats>
</div>
);
}
}
export default DataAnalytics;
Child :
class Stats extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="stats" style={{textAlign:'center'}}>
<h4>Number: </h4>
</div>
);
}
componentDidUpdate() {
var data = this.props.data;
if(!jQuery.isEmptyObject(data)) {
$( ".stats" ).html("<h4>Number : data['nb']['counterIn']</h4>");
}
}
}
export default Stats;
Anyone know how to refresh automatically my React component.
The React component doesn't update because it doesn't realize that it's state changes. You can force an update on a React component by creating it each time with a different key attribute.
render() {
return (
<div id="dataAnalytics">
<Stats key={this.uniqueId()} data={this.state.data}></Stats>
</div>
);
}
// Example of a function that generates a unique ID each time
uniqueId: function () {
return new Date().getTime();
}
I usually do it like -
function MyComponent() {
const [_, refresh] = useState()
useEffect(() => {
// Code that's supposed to run on refresh
}, [refresh])
return
<>
{/* Rest of the code */}
<button onclick={() => refresh(true)}>Refresh</button>
</>
}
The idea is to define a state and use it as a dependency of useEffects (or useMemos and useCallbacks).
If there are multiple effect hooks, add refresh to all of them as a dependency.