List not rendering data - reactjs

I'm rendering a list of unread messages from facebook, it was working just fine last time I opened it, now it wouldn't render anything even though I'm getting a response from facebook !
import React, {Component} from 'react';
import { List, Button, Avatar, Icon } from 'antd';
const IconText = ({ type, text }) => (
<span>
<Icon type={type} style={{ marginRight: 8 }} />
{text}
</span>
);
class Notifications extends Component {
constructor(props) {
super(props);
this.state={
initLoading: true,
loading: false,
UnreadMessages:[],
pageAccessToken:""
};
}
componentWillMount(){
window.FB.api(
'/me',
'GET',
{"fields":"conversations{unread_count,messages{from,message}}","access_token":this.state.pageAccessToken},
function(response) {
console.log(response)
if (response.conversations) {
let listData = [];
for (let i = 0; i < response.conversations.data.length; i++) {
if(response.conversations.data[i].unread_count!==0){
listData.push({
from:response.conversations.data[i].messages.data[0].from.name,
message: response.conversations.data[i].messages.data[0].message,
})
}else i++;
}
this.setState({ UnreadMessages: listData });
}
else {
console.log(response.error);
}
}.bind(this)
);
}
render() {
const {UnreadMessages } = this.state;
return (
<div>
<List
itemLayout="horizontal"
dataSource={UnreadMessages}
loading={UnreadMessages.length ? false : true}
renderItem={item => (
<List.Item
actions={[<IconText type="facebook" />, <IconText type="message" />]}
>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={item.from}
description={item.message}
/>
</List.Item>
)}
/>
</div>
);
}
}
export default Notifications;
I really couldn't find the issue, I used the same structure for another list and it works just fine !

Try this code. I think the issue is in handling response directly inside window.FB.api call, so this.setState is not setting the state ,because this is not referring to the component.
componentWillMount(){
window.FB.api(
'/me',
'GET',
{"fields": "conversations{unread_count,messages{from,message}}", "access_token": this.state.pageAccessToken})
.then( (response) => {
console.log(response)
if (response.conversations) {
let listData = [];
for (let i = 0; i < response.conversations.data.length; i++) {
if(response.conversations.data[i].unread_count!==0){
listData.push({
from:response.conversations.data[i].messages.data[0].from.name,
message: response.conversations.data[i].messages.data[0].message,
})
}else i++;
}
this.setState({UnreadMessages: listData });
} else { console.log(response.error) }
})

Updated Version with arrow function to solve context issue - FB.api does not support promises, unfortunately. Also, else i++ does not make sense, i will get increased in the loop anyway:
componentWillMount() {
const {pageAccessToken} = this.state;
window.FB.api(
'/me',
{fields: 'conversations{unread_count,messages{from,message}}', access_token: pageAccessToken}, (response) => {
console.log(response);
if (response.conversations) {
let listData = [];
for (let i = 0; i < response.conversations.data.length; i++) {
if (response.conversations.data[i].unread_count !== 0) {
listData.push({
from: response.conversations.data[i].messages.data[0].from.name,
message: response.conversations.data[i].messages.data[0].message,
});
}
}
this.setState({UnreadMessages: listData});
} else {
console.log(response.error);
}
});
}
I´ve also improved some stuff (missing semicolons, unneccessary "GET", only single quotes, ...)

Related

What's wrong with my method call I try to learn React and must pass component and Props to child

I learn Reactjs and javascript and wanted to call this withFetching Component but don't understand how to set the arguments correctly. I understand overall logic but still learning the details
Here is the switch where I call the withFetching
render() {
const theFile = encodeURI(`./images/${fileData}`);
switch (mediaType) {
case 'xlsx': {
const newProps = { ...this.props, responseType: 'arraybuffer' };
return (
<div className="pg-viewer-wrapper">
<div className="pg-viewer" id="pg-viewer">
<{withFetching(XlsxViewer, newProps, fileType="xlsx", filePath={theFile} )}/>
</div>
</div>
);
}
.........
I try like this also:(making WithFetching camel-case even it's a function)
return (
<div className="pg-viewer-wrapper">
<div className="pg-viewer" id="pg-viewer">
<WithFetching XlsxViewer={XlsxViewer} newProps={newProps} />
</div>
</div>
);
But the WithFetching constructor never firers!
I try like this:
case 'xlsx': {
const newProps = { ...this.props, responseType: 'arraybuffer', fileType: 'xlsx', filePath: { theFile } };
// return withFetching(XlsxViewer, newProps);
return (
<div className="pg-viewer-wrapper">
<div className="pg-viewer" id="pg-viewer">
{WithFetching(XlsxViewer, newProps)};
</div>
</div>
);
}
But still the WithFetching constructor never firers!
Error: (yea I know the way I use brackets are my mistake it's hard to learn)
And this is the withFetching that is in its own file called fetch-wrapper.jsx. The WrappedComponent argument is the above XlsxViewer that is the final "On-screen" Component!
import React, { Component } from 'react';
import Error from './error';
import Loading from './loading';
function withFetching(WrappedComponent, props) {
return class FetchComponent extends Component {
constructor(props) {
// eslint-disable-line no-shadow
super(props);
this.state = {};
this.xhr = this.createRequest(props.filePath);
}
componentDidMount() {
try {
this.fetch();
} catch (e) {
if (this.props.onError) {
this.props.onError(e);
}
this.setState({ error: 'fetch error' });
}
}
componentWillUnmount() {
this.abort();
}
createRequest(path) {
let xhr = new XMLHttpRequest();
if ('withCredentials' in xhr) {
// XHR for Chrome/Firefox/Opera/Safari.
xhr.open('GET', path, true);
// } else if (typeof XDomainRequest !== 'undefined') {
// // XDomainRequest for IE.
// xhr = new XDomainRequest();
// xhr.open('GET', path);
} else {
// CORS not supported.
xhr = null;
return null;
}
if (props.responseType) {
xhr.responseType = props.responseType;
}
xhr.onload = () => {
if (xhr.status >= 400) {
this.setState({ error: `fetch error with status ${xhr.status}` });
return;
}
const resp = props.responseType ? xhr.response : xhr.responseText;
this.setState({ data: resp });
};
return xhr;
}
fetch() {
this.xhr.send();
}
abort() {
if (this.xhr) {
this.xhr.abort();
}
}
render() {
if (!this.xhr) {
return <h1>CORS not supported..</h1>;
}
if (this.state.error) {
return <Error {...this.props} error={this.state.error} />;
}
if (this.state.data) {
return <WrappedComponent data={this.state.data} {...this.props} />;
}
return <Loading />;
}
};
}
export default withFetching;
And this the final XlxsViewer Component that will be visible.
Thanks to Copyright (c) 2017 PlanGrid, Inc.
import React, { Component } from 'react';
import XLSX from 'xlsx';
import CsvViewer from './csv-viewer';
class XlxsViewer extends Component {
constructor(props) {
super(props);
this.state = this.parse();
}
parse() {
const dataArr = new Uint8Array(this.props.data);
const arr = [];
for (let i = 0; i !== dataArr.length; i += 1) {
arr.push(String.fromCharCode(dataArr[i]));
}
const workbook = XLSX.read(arr.join(''), { type: 'binary' });
const names = Object.keys(workbook.Sheets);
const sheets = names.map(name => XLSX.utils.sheet_to_csv(workbook.Sheets[name]));
return { sheets, names, curSheetIndex: 0 };
}
renderSheetNames(names) {
const sheets = names.map((name, index) => (
<input
key={name}
type="button"
value={name}
onClick={() => {
this.setState({ curSheetIndex: index });
}}
/>
));
return <div className="sheet-names">{sheets}</div>;
}
renderSheetData(sheet) {
const csvProps = Object.assign({}, this.props, { data: sheet });
return <CsvViewer {...csvProps} />;
}
render() {
const { sheets, names, curSheetIndex } = this.state;
return (
<div className="spreadsheet-viewer">
{this.renderSheetNames(names)}
{this.renderSheetData(sheets[curSheetIndex || 0])}
</div>
);
}
}
export default XlxsViewer;

React componentDid mount get Async image. if not done before changing page, how to cancel the request?

I am kinda new to react coming from an Angular background. recently I am building a page where I display cards that all need to fetch an image from the microsoft graph api. The return type of the call is a base64 string. I don't want to put this into redux because it will make my devtools unreadable.
Therefore I decided to make a async call in the componentDidMount lifecycle hook with the this.isMounted pattern (Cancel All Subscriptions and Asyncs in the componentWillUnmount Method, how?). The problem however is that for some reason this doesn't unsubscribe the async call that I made. I am not sure whether I made a mistake or whether it needs to be unsubscribed instead of checking whether the component is mounted. but I cannot find any information on how to deal with this.
Any help would be appreciated.
My teamCard code:
import React from "react";
import { Icon } from "office-ui-fabric-react/lib/Icon";
import TeamCardLogo from "./teamCardLogo/teamCardLogo";
import TeamCardPersona from "./teamCardPersona/teamCardPersona";
import { GetGroupMembers } from "../../HttpRepositories/graphRepository";
import { FormattedMessage } from "react-intl";
import Fade from "react-reveal/Fade";
import { Modal } from "office-ui-fabric-react/lib/Modal";
import reactAppInsights from "react-appinsights";
import TeamModal from "./teamModal/teamModal";
class TeamCard extends React.Component {
state = {
members: "",
modelIsOpen: false
};
async componentDidMount() {
let members = await GetGroupMembers(this.props.id);
if (this.state.member !== "error") {
this.setState({ members });
}
}
_openModal = id => {
this.setState({ modelIsOpen: true });
};
_closeModal = () => {
this.setState({ modelIsOpen: false });
};
render() {
let members = "";
if (
typeof this.state.members !== "undefined" &&
this.state.members.length > 0 &&
this.state.members !== "error"
) {
members = this.state.members.map((member, i) => {
if (i < 5) {
return (
<div className="team-card-body__personas-wrapper-person" key={i}>
<TeamCardPersona
className="team-card-body__personas-wrapper-person"
member={member}
key={i}
/>
</div>
);
}
});
} else {
members = <div className="no-members-spacer" />;
}
let favouriteIcon = "";
this.props.isFavorite === true
? (favouriteIcon = <Icon iconName="FavoriteStarFill" />)
: (favouriteIcon = <Icon iconName="FavoriteStar" />);
return (
<React.Fragment>
{/* <Fade bottom delay={this.props.delay} appear={true}> */}
<article
className="team-card-wrapper"
onClick={() => this._openModal(this.props.id)}
>
<header className="team-card-wrapper__header">
<TeamCardLogo
injectClass="team-card-wrapper__header-photo"
teamId={this.props.id}
/>
<div className="team-card-wrapper__header-options-wrapper">
<div className="header-options__icon-group">
<div className="header-options__group-type">
<Icon iconName="LockSolid" />
</div>
</div>
<div className="header-options__icon-group">
<div className="header-options__favourite">{favouriteIcon}</div>
</div>
</div>
</header>
<section className="team-card-body">
<h1>{this.props.title}</h1>
<h2>
{" "}
<FormattedMessage
id="teamcard.memberCount"
defaultMessage="Leden"
/>
:{this.state.members.length}
</h2>
<div className="team-card-body__personas-wrapper">{members}</div>
<p className="description">{this.props.description}</p>
{/* <div className="team-card-body__join-button-wrapper">
<PrimaryButton text="Lid worden" />
</div> */}
</section>
</article>
{/* </Fade> */}
<Modal
titleAriaId={this._titleId}
subtitleAriaId={this._subtitleId}
isOpen={this.state.modelIsOpen}
onDismiss={this._closeModal}
isBlocking={false}
containerClassName="team-modal-wrapper"
>
<TeamModal
teamId={this.props.id}
title={this.props.title}
description={this.props.description}
favorite={this.props.isFavorite}
members={this.state.members}
closeModal={this._closeModal}
/>
</Modal>
</React.Fragment>
);
}
}
export default TeamCard;
my TeamCardLogo code (makes the async call)
import React from "react";
import { Icon } from "office-ui-fabric-react/lib/Icon";
import { getImage } from "../../../HttpRepositories/graphRepository";
class TeamCardImage extends React.Component {
constructor(props) {
super(props);
this._isMounted = false;
}
state = {
groupImage: ""
};
getLogo = () => {};
async componentDidMount() {
this._isMounted = true;
if (this._isMounted) {
let logo = await getImage(
`https://graph.microsoft.com/v1.0/groups/${
this.props.teamId
}/photo/$value`
);
if (logo !== "error") {
this.setState({ groupImage: logo });
}
}
}
render() {
let injectedClassName =
this.props.injectClass != "" ? this.props.injectClass : "";
let headerPhoto = "";
const groupIcon = (
<div className="team-card-wrapper__header-photo-alt">
<Icon iconName="Group" />
</div>
);
if (this.state.groupImage === "") {
headerPhoto = groupIcon;
} else {
headerPhoto = <img src={this.state.groupImage} alt=" " />;
}
return (
<React.Fragment>
<div className={injectedClassName}>{headerPhoto}</div>
</React.Fragment>
);
}
componentWillUnmount() {
this._isMounted = false;
}
}
export default TeamCardImage;
my httpRepos code
import { getGraphToken } from "../adalConfig";
import axios from "axios";
export const GetGroupMembers = async groupId => {
// we initiate a new token, to be sure that it didn't expire.
let graphToken = getGraphToken();
try {
let response = await axios({
url: `https://graph.microsoft.com/v1.0/groups/${groupId}/members?$select=id,displayName`,
method: "GET",
headers: { Authorization: "Bearer " + graphToken }
});
if (response.status != 200 && response.status != 204) {
return "error";
}
return await response.data.value;
} catch (error) {
return "error";
}
};
export const getImage = async url => {
// we initiate a new token, to be sure that it didn't expire.
let graphToken = getGraphToken();
try {
let response = await axios({
url: url,
method: "get",
responseType: "blob",
headers: { Authorization: "Bearer " + graphToken }
});
if (response.status != 200 && response.status != 204) {
return "error";
}
var urlCreator = window.URL || window.webkitURL;
var imageUrl = urlCreator.createObjectURL(response.data);
return await imageUrl;
} catch (error) {
return "error";
}
};
You want to check that _isMounted is still true before you call setState, not before you start the request.
async componentDidMount() {
this._isMounted = true;
let logo = await getImage(
`https://graph.microsoft.com/v1.0/groups/${this.props.teamId}/photo/$value`
);
if (this._isMounted && logo !== "error") {
this.setState({ groupImage: logo });
}
}

React HighChart Example

I m using React-js-highcharts, and i have done the below code, getting data e.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
error.
Kindly help me to sort out this.
Have followed this example.
https://github.com/whawker/react-jsx-highcharts/blob/gh-pages/examples/SimpleLine/App.js
import React from 'react';
import Highcharts from 'highcharts';
import {
HighchartsChart, Chart, withHighcharts, XAxis, YAxis, Title, Subtitle, Legend, LineSeries
} from 'react-jsx-highcharts';
import { extent as d3ArrayExtent } from 'd3-array';
const plotOptions = {
series: {
pointStart: 2010
}
};
class ProductHealth extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
}
}
componentDidMount() {
let defaultOptions = {
method:'GET',
headers:{
Accept: 'application/json',
}
};
fetch('http://localhost:3000/health', defaultOptions)
.then(function(response) {
// The response is a Response instance.
// You parse the data into a useable format using `.json()`
let resData = response.json();
return resData;
}).then(response => {
console.log("Response",response);
this.processResponseData(response);
}).catch(function(error){ console.log(error) })
}
processResponseData = (response) => {
let apiValues = [];
response.forEach(element => {
let value = [];
element.values.forEach(item => {
value.push(
[item.count]
);
})
apiValues.push({
name : element.api,
value : value
})
});
this.setState({
data : apiValues
});
};
renderLineChart() {
return this.state.data.map((lineData, index) => {
return (
<LineSeries key={index} name={lineData.api} data={lineData.value} />
)
})
}
render() {
if (this.state.data.length > 0) {
return (
<div>
<HighchartsChart plotOptions={plotOptions}>
<Chart />
<Title>Visa Direct Data</Title>
<Legend layout="vertical" align="right" verticalAlign="middle" />
<XAxis>
<XAxis.Title>Time</XAxis.Title>
</XAxis>
<YAxis>
<YAxis.Title>API Calls</YAxis.Title>
{this.renderLineChart()}
</YAxis>
</HighchartsChart>
</div>
);
} else {
return <div>Loading...</div>;
}
}
}
export default withHighcharts(ProductHealth, Highcharts);
I strongly recommend you to try to use our official React wrapper which is also available to download through npm.
Here is the link to package: https://www.npmjs.com/package/highcharts-react-official
GitHub repository: https://github.com/highcharts/highcharts-react
I know I'm 7 months late to this, but looks like the clue lies in
You may have returned undefined, an array or some other invalid object
You are returning this.state.data.map which will return an array.
I would suggest you try
renderLineChart () {
const { data } = this.state
return (
<Fragment>
{data.map((lineData, index) => (
<LineSeries key={index} name={lineData.api} data={lineData.value} />
)}
</Fragment>
)
}

React setState fetch API

I am starting to learn React and creating my second project at the moment. I am trying to usi MovieDb API to create a movie search app. Everything is fine when I get the initial list of movies. But onClick on each of the list items I want to show the details of each movie. I have created a few apps like this using vanilla JS and traditional XHR call. This time I am using fetch API which seems straightforward ans simply to use, however when I map through response data to get id of each movie in order to retrieve details separately for each of them I get the full list of details for all the items, which is not the desired effect. I put the list of objects into an array, because after setState in map I was only getting the details for the last element. I know that I am probably doing something wrong within the API call but it might as well be my whole REACT code. I would appreciate any help.
My code
App.js
import React, { Component } from 'react';
import SearchInput from './Components/SearchInput'
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state =
{
value: '',
showComponent: false,
results: [],
images: {},
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleOnChange = this.handleOnChange.bind(this);
this.getImages = this.getImages.bind(this);
this.getData = this.getData.bind(this);
}
ComponentWillMount() {
this.getImages();
this.getData();
}
getImages(d) {
let request = 'https://api.themoviedb.org/3/configuration?api_key=70790634913a5fad270423eb23e97259'
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
this.setState({
images: data.images
});
});
}
getData() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.state.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
this.setState({
results: data.results
});
});
}
handleOnChange(e) {
this.setState({value: e.target.value})
}
handleSubmit(e) {
e.preventDefault();
this.getImages();
this.setState({showComponent: true});
this.getData();
}
render() {
return (
<SearchInput handleSubmit={this.handleSubmit} handleOnChange={this.handleOnChange} results={this.state.results} images={this.state.images} value={this.state.value} showComponent={this.state.showComponent}/>
);
}
}
export default App;
SearchInput.js
import React, {Component} from 'react';
import MoviesList from './MoviesList';
class SearchInput extends Component {
render() {
return(
<div className='container'>
<form id='search-form' onSubmit={this.props.handleSubmit}>
<input value={this.props.value} onChange={this.props.handleOnChange} type='text' placeholder='Search movies, tv shows...' name='search-field' id='search-field' />
<button type='submit'>Search</button>
</form>
<ul>
{this.props.showComponent ?
<MoviesList value={this.props.value} results={this.props.results} images={this.props.images}/> : null
}
</ul>
</div>
)
}
}
export default SearchInput;
This is the component where I try to fetch details data
MovieList.js
import React, { Component } from 'react';
import MovieDetails from './MovieDetails';
let details = [];
class MoviesList extends Component {
constructor(props) {
super(props);
this.state = {
showComponent: false,
details: []
}
this.showDetails = this.showDetails.bind(this);
this.getDetails = this.getDetails.bind(this);
}
componentDidMount() {
this.getDetails();
}
getDetails() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.props.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
data.results.forEach((result, i) => {
let url = 'https://api.themoviedb.org/3/movie/'+ result.id +'?api_key=70790634913a5fad270423eb23e97259&append_to_response=videos,images';
return fetch(url)
.then((response) => {
return response.json();
}).then((data) => {
details.push(data)
this.setState({details: details});
});
});
console.log(details);
});
}
showDetails(id) {
this.setState({showComponent: true}, () => {
console.log(this.state.details)
});
console.log(this.props.results)
}
render() {
let results;
let images = this.props.images;
results = this.props.results.map((result, index) => {
return(
<li ref={result.id} id={result.id} key={result.id} onClick={this.showDetails}>
{result.title}{result.id}
<img src={images.base_url +`${images.poster_sizes?images.poster_sizes[0]: 'err'}` + result.backdrop_path} alt=''/>
</li>
)
});
return (
<div>
{results}
<div>
{this.state.showComponent ? <MovieDetails details={this.state.details} results={this.props.results}/> : null}
</div>
</div>
)
}
}
export default MoviesList;
MovieDetails.js
import React, { Component } from 'react';
class MovieDetails extends Component {
render() {
let details;
details = this.props.details.map((detail,index) => {
if (this.props.results[index].id === detail.id) {
return(
<div key={detail.id}>
{this.props.results[index].id} {detail.id}
</div>
)} else {
console.log('err')
}
});
return(
<ul>
{details}
</ul>
)
}
}
export default MovieDetails;
Theres a lot going on here...
//Here you would attach an onclick listener and would fire your "get details about this specific movie function" sending through either, the id, or full result if you wish.
//Then you getDetails, would need to take an argument, (the id) which you could use to fetch one movie.
getDetails(id){
fetch(id)
displayresults, profit
}
results = this.props.results.map((result, index) => {
return(
<li onClick={() => this.getDetails(result.id) ref={result.id} id={result.id} key={result.id} onClick={this.showDetails}>
{result.title}{result.id}
<img src={images.base_url +`${images.poster_sizes?images.poster_sizes[0]: 'err'}` + result.backdrop_path} alt=''/>
</li>
)
});
Thanks for all the answers but I have actually maanged to sort it out with a bit of help from a friend. In my MovieList I returned a new Component called Movie for each component and there I make a call to API fro movie details using each of the movie details from my map function in MovieList component
Movielist
import React, { Component } from 'react';
import Movie from './Movie';
class MoviesList extends Component {
render() {
let results;
if(this.props.results) {
results = this.props.results.map((result, index) => {
return(
<Movie key={result.id} result={result} images={this.props.images}/>
)
});
}
return (
<div>
{results}
</div>
)
}
}
export default MoviesList;
Movie.js
import React, { Component } from 'react';
import MovieDetails from './MovieDetails';
class Movie extends Component {
constructor(props) {
super(props);
this.state = {
showComponent: false,
details: []
}
this.showDetails = this.showDetails.bind(this);
this.getDetails = this.getDetails.bind(this);
}
componentDidMount() {
this.getDetails();
}
getDetails() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.props.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
let url = 'https://api.themoviedb.org/3/movie/'+ this.props.result.id +'?api_key=70790634913a5fad270423eb23e97259&append_to_response=videos,images';
return fetch(url)
}).then((response) => {
return response.json();
}).then((data) => {
this.setState({details: data});
});
}
showDetails(id) {
this.setState({showComponent: true}, () => {
console.log(this.state.details)
});
}
render() {
return(
<li ref={this.props.result.id} id={this.props.result.id} key={this.props.result.id} onClick={this.showDetails}>
{this.props.result.title}
<img src={this.props.images.base_url +`${this.props.images.poster_sizes?this.props.images.poster_sizes[0]: 'err'}` + this.props.result.backdrop_path} alt=''/>
{this.state.showComponent ? <MovieDetails details={this.state.details}/> : null}
</li>
)
}
}
export default Movie;

Display a limited number of items in the filtered array with a load more button

I have this component Cat that pulls the data from a local json file and displays all the Cats from the file in the alphabetical order. I need to display first 10 cats, and then have a Load More button to display the rest. Does anyone have a solution on how to do it in a good way? The line {providerNumber.length} Cats still needs to show the total number of cats, not the first 10.
Thank you!
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import Error from './Error.jsx';
export default class Cat extends React.Component{
constructor() {
super();
this.state = {
providersData: [],
loading: true
};
}
componentDidMount () {
setTimeout(() => this.setState({ loading: false }), 500);
fetch('../feed/sample.json')
.then(response => { console.log(response); return response.json()})
.then(responseData => {
console.log(responseData)
this.setState({ providersData: [...responseData.providers].sort((a,b) => {
const aName = a.companyName.toUpperCase()
const bName = b.companyName.toUpperCase()
if (aName < bName) {
return -1;
}
if (aName > bName) {
return 1
}
// names must be equal
return 0
})
});
})
.catch(error => {
console.log('Error fetching and parsing data', error);
});
}
render() {
const { loading } = this.state;
const providerNumber = this.state.providersData.filter(provider => provider.yearStarted >= 2010 && provider.type === 'cat')
if(loading) {
return (
<div> <img src="./../assets/loader.svg" alt=""/></div>
); // render loading when app is not ready
}
return this.state.providersData.length ? (
<div>
<h1>Cats</h1>
<div> {providerNumber.length} Cats</div>
{this.state.providersData.map(function(provider, index) {
if (provider.yearStarted >= 2010 && provider.type === 'cat') {
return (
<div key={index} className="job">
<h2>{provider.companyName}</h2>
<img src={provider.images['Company Logo'].url} alt=""/>
</div>
)
}
})}
</div>
) : <Error />
}
};
You can do something like this before you call .map in your render:
this.state.providersData.slice(0, this.state.numberOfCatsShown).map(...)
You would need to initialize this.state.numberOfCatsShown to 10 in your constructor first.
When you want to display more cats, use a function that looks like this:
showMoreCats() {
const newNumberOfCatsShown = this.state.numberOfCatsShown + 10;
// set it to the length of the array to show all the cats.
this.setState({ numberOfCatsShown: newNumberOfCatsShown });
}

Resources