At the moment when 'scroll' is at the very bottom, the function getUsers () is called. How to set the scroll so that it doesn't reach the end of the slider, and the getUsers () function is called. That there would be an infinity scroll effect. I mean the scroll effect like here: https://codesandbox.io/s/ww7npwxokk. When the scroll reaches the bottom, it goes back.
Code here: https://stackblitz.com/edit/react-nq8btq
import './style.css';
import axios from 'axios';
class App extends Component {
constructor() {
super();
this.state = {
users: [],
page: 1
};
}
componentDidMount() {
this.getUsers();
}
getUsers = () => {
axios({
url: `https://jsonplaceholder.typicode.com/users`,
method: "GET"
})
.then(res => {
this.setState({
users: res.data
});
})
.catch(error => {
console.log(error);
})
}
scroll = (e) => {
const page = this.state.page;
const bottom = e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight;
if (bottom) {
alert('bottom');
this.getUsers()
this.setState({
page: this.state.page + 1
})
}
const top = e.target.scrollTop;
if(top === 0 && page > 1) {
alert('I AM AT THE TOP');
this.setState({
page: this.state.page - 1
})
}
}
render() {
console.log(this.state.page)
console.log(this.state.users)
return (
<div>
<div onScroll={this.scroll} className="container">
<ul>
{this.state.users.map((user, index) =>
<li>
{user.name}
</li>
)}
</ul>
</div>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Here I've updated your code, slightly simplified, but mostly your code with the key points commented.
class App extends Component {
state = {
users: [],
page: 1
};
componentDidMount() {
this.getUsers();
}
getUsers = () => {
axios({
url: `https://jsonplaceholder.typicode.com/users`,
method: "GET"
})
.then(res => {
this.setState({
// *you must append to users in state, otherwise
// the list will not grow as the user scrolls
users: [...this.state.users, ...res.data],
page: this.state.page + 1
});
})
.catch(error => {
console.log(error);
})
}
scroll = (e) => {
// *simplified, to only handle appending to the list
// note the 50px from the bottom, adjust as required
// so that the request is made before the users reaches
// the bottom of the page under normal scrolling conditions.
if (e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50) {
this.getUsers();
}
}
render() {
return (
<div onScroll={this.scroll} className="container">
<ul>
{this.state.users.map((user, index) =>
// *always add a unique key for each item
<li key={user.name}>
{user.name}
</li>
)}
</ul>
</div>
);
}
}
All you need to do is decide when to call get users.
i.e. if you want it to be called when you have already scrolled through 70% the height then the factor would be 0.3 (30%)
Whenever the scroll exceeds the 70% mark this.getusers() would be called.
Check out how I have modified the same condition written by you.
Rest of the code remains the same
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import axios from 'axios';
class App extends Component {
constructor() {
super();
this.state = {
users: [],
page: 1
};
}
componentDidMount() {
this.getUsers();
}
getUsers = () => {
axios({
url: `https://jsonplaceholder.typicode.com/users`,
method: "GET"
})
.then(res => {
this.setState({
users: res.data
});
})
.catch(error => {
console.log(error);
})
}
scroll = (e) => {
const page = this.state.page;
const bottom = e.target.scrollHeight - e.target.scrollTop < e.target.clientHeight-0.3*e.target.clientHeight;
if (bottom) {
alert('bottom');
this.getUsers()
this.setState({
page: this.state.page + 1
})
}
const top = e.target.scrollTop;
if(top === 0 && page > 1) {
alert('I AM AT THE TOP');
this.setState({
page: this.state.page - 1
})
}
}
render() {
console.log(this.state.page)
console.log(this.state.users)
return (
<div>
<div onScroll={this.scroll} className="container">
<ul>
{this.state.users.map((user, index) =>
<li>
{user.name}
</li>
)}
</ul>
</div>
</div>
);
}
}
render(<App />, document.getElementById('root'));
This lib should do the trick for you
$ yarn add react-infinite-scroller
Very simple to implement
<InfiniteScroll
pageStart={0}
loadMore={this.getUsers()}
hasMore={this.state.hasMoreUsers}
loader={<div key={0}>loading ...</div>}
>
<li>
{user.name}
</li>
</InfiniteScroll>
Don't forget to import the lib in your component file
import InfiniteScroll from 'react-infinite-scroller';
Related
I have a question regarding how I can fix 'Can't perform a React state update' error message. I did read a little further into the issue and states it is only a warning. Please note, I'm a beginner at React. Here is the problem as follows.
I have a header component that is comprised of a navbar that has two states for toggling the mobile navigation menu button and another for changing background-color on scroll. I implemented navbar toggle functionality and I started receiving the so-called error in JS console. Upon further inspection, I have determined that it is something to do with my toggle state. Any help will be appreciated. Thanks in advance!
import React, { useState } from 'react';
import { Icon } from 'react-icons-kit';
import {bars} from 'react-icons-kit/fa/bars';
import {times} from 'react-icons-kit/fa/times';
import {chevronCircleDown} from 'react-icons-kit/fa/chevronCircleDown';
const Header = (props) => {
const [toggle, setToggle] = useState(false);
const [navbar, setNavbar] = useState(false);
const handleToggle = () => {
setToggle(!toggle);
}
const changeBackground = () => {
if(window.scrollY >= 60) {
setNavbar(true);
}
else {
setNavbar(false);
}
}
window.addEventListener('scroll', changeBackground);
if(props.data){
var description = props.data.description;
var navigation = props.data.navigation.map(item => {
return <li key={item.linkname} className="nav-item"><a href={item.href} className={item.className}>{item.linkname}</a></li>
});
}
return (
<header id="home" className="main-header">
<div className="container">
<nav className={navbar ? 'navbar nav-bg' : 'navbar'} aria-label="Main Navigation" id="navbar">
<ul className={toggle ? 'navbar-nav active' : 'navbar-nav'} id="nav">
{navigation}
</ul>
<button className="btn-mobile-nav" type="button" aria-controls="nav" aria-expanded={toggle ? 'true' : 'false'} aria-label="Mobile Navigation button" title="Mobile menu button" onClick={handleToggle}>{toggle ? <Icon icon={times} size={24} title="Close Menu"/> : <Icon icon={bars} size={24} title="Open Menu"/> }</button>
</nav>
</div>
<div className="header-content d-flex flex-column">
<div>
<h1 className="header-title"><span className="typed"></span></h1>
<p className="header-summary">{description}</p>
</div>
</div>
<Icon icon={chevronCircleDown} size={54}/>
</header>
);
}
export default Header;
import React from 'react';
import SkipNav from './Components/SkipNav';
import Header from './Components/Header';
import Footer from './Components/Footer';
import About from './Components/About';
import Resume from './Components/Resume';
import Portfolio from './Components/Portfolio';
import Contact from './Components/Contact';
class App extends React.Component {
constructor(props){
super(props);
this.state = {
resumeData: [],
recipName: '',
recipEmail: '',
recipSubject: '',
recipMessage: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleCaptchaChange = this.handleCaptchaChange.bind(this);
this.handleEmailSent = this.handleEmailSent.bind(this);
}
getResumeData = () => {
fetch('/data.json')
.then(response => {
return response.json()
})
.then(data => {
this.setState({
resumeData: data
});
})
.catch(error => {
console.log(error)
alert(`Unable to retrieve data! See JS console for details. Error:${error}`)
})
}
handleChange = (event) => {
this.setState({ [event.target.name]: event.target.value });
}
handleCaptchaChange = (value) => {
console.log("Captcha value:", value);
}
handleEmailSent = (event) => {
event.preventDefault();
if (this.state.recipName === '' || this.state.recipEmail === '' || this.state.recipSubject === '' || this.state.recipMessage === '') {
console.log('All fields required!')
alert('All fields are required!');
return;
}
let data = {
recipName: this.state.recipName,
recipEmail: this.state.recipEmail,
recipSubject: this.state.recipSubject,
recipMessage: this.state.recipMessage
};
console.log(data);
fetch (`https://api.eahassan.me/sendEmail`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
}).then((response) => {
console.log(response.data);
alert("E-Mail sent successfully!");
window.location.reload();
})
.catch((error) => console.log("E-Mail Failure - Error:", error));
}
componentDidMount = () => {
this.getResumeData();
}
render() {
return (
<div className="App">
<SkipNav title="Skip to main content"/>
<Header data={this.state.resumeData.main}/>
<main id="mainContent">
<About data={this.state.resumeData.main} title="About Me"/>
<Resume data={this.state.resumeData.resume} eduTitle="Education" workTitle="Work" skillTitle="Skills"/>
<Portfolio data={this.state.resumeData.portfolio}/>
<Contact data={this.state.resumeData.main} recommendData={this.state.resumeData.recommendations} captchaChange={this.handleCaptchaChange} recipName={this.state.recipName} recipEmail={this.state.recipEmail} recipSubject={this.state.recipSubject} recipMessage={this.state.recipMessage} EmailSend={this.handleEmailSent} change={this.handleChange}/>
</main>
<Footer data={this.state.resumeData.main}/>
</div>
);
}
}
export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
You are unconditionally adding a scroll event listener in the body of the component. This should be added in an useEffect hook and cleaned up when the component unmounts. For scroll events that are generally very noisy, you'll want to make these passive listeners.
useEffect(() => {
const changeBackground = () => {
setNavbar(window.scrollY >= 60);
}
window.addEventListener('scroll', changeBackground, { passive: true });
return () => window.removeEventListener('scroll', changeBackground, { passive: true });
}, []);
The App component's constructor function was being rendered twice.
I removed <React.StrictMode> from index.js and the error went away.
Problem solved!
https://arange.github.io/2020-06-15-react-component-s-constructor-rendered-twice-leading-to-bugs-using-axios-interceptor/
after onclick event occurs in backpackList.js, fetch data in context.js and then through setState I want to update noneUserCart . After that i want to get data from context.js to backpackList.js to show web page. but the data is inital data []. How can I solve this problem?!
I think this is a Asynchronous problem, but I'm new react, so I don't know how to write code for this. or do I use async, await.
Help me please!
import React, { Component } from 'react';
const ProductContext = React.createContext();
const ProductConsumer = ProductContext.Consumer;
class ProductProvider extends Component {
constructor() {
super();
this.state = {
totalProducts: 0,
isLogin: false,
cartList: [],
isNavOpen: false,
isCartOpen: false,
noneUserCart: [],
};
}
noneUserAddCart = bagId => {
fetch('/data/getdata.json', {
method: 'GET',
})
.then(res => res.json())
.catch(err => console.log(err))
.then(data => {
this.setState(
{
noneUserCart: [...this.state.noneUserCart, data],
},
() => console.log(this.state.noneUserCart)
);
});
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
handleCart: this.handleCart,
getToken: this.getToken,
addNoneUserCart: this.addNoneUserCart,
hanldeCheckout: this.hanldeCheckout,
openNav: this.openNav,
showCart: this.showCart,
habdleCartLsit: this.habdleCartLsit,
deleteCart: this.deleteCart,
noneUserAddCart: this.noneUserAddCart,
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
export { ProductProvider, ProductConsumer };
import React, { Component } from 'react';
import { ProductConsumer } from '../../context';
export default class BackpackList extends Component {
render() {
const {
backpackdata,
backdescdata,
isdescOpen,
showDesc,
descClose,
rangenumone,
rangenumtwo,
} = this.props;
return (
<div>
{backdescdata.map((bag, inx) => {
return (
<>
{isdescOpen && bag.id > rangenumone && bag.id < rangenumtwo && (
<div className="listDescContainer" key={inx}>
<div className="listDescBox">
<ProductConsumer>
{value => (
<div
className="cartBtn"
onClick={() => {
const token = value.getToken();
if (token) {
value.handleCart(bag.id, token);
} else {
value.noneUserAddCart(bag.id);
console.log(value.noneUserCart);
// this part. value.noneUserCart is undefined
}
}}
>
add to cart.
</div>
)}
</ProductConsumer>
<span className="descClosebtn" onClick={descClose}>
X
</span>
</div>
</div>
</div>
)}
</>
);
})}
</div>
);
}
}
fetch is asynchronous, this.setState is yet called when console.log
<div
className="cartBtn"
onClick={() => {
const token = value.getToken();
if (token) {
value.handleCart(bag.id, token);
} else {
value.noneUserAddCart(bag.id);
console.log(value.noneUserCart);
// this part. value.noneUserCart is undefined
}
}}
>
add to cart.
{value.noneUserCart}
{/* when finished, result should show here */}
</div>
I'm trying to add the notable residents's names listed for every planet that is clicked using this API: https://swapi.dev/
I tried doing it at the bottom of the Planet detail page, but it is not working.
The notable residents are links to other API resources, and I don't know how to deal with them.
ERROR:
TypeError: Cannot read property 'map' of undefined
PlanetDetail.render
C:/Users/charl/Desktop/IRONHACK/Paperbox/paperbox/src/pages/Planetdetail.js:75
72 | )}
73 | </div>
74 | <div>
> 75 | <h1>Notable people</h1>
| ^ 76 | {
77 | this.state.planetInfo.residents.map(resident =>
78 | <p>{resident.name}</p>
HOME.JS
import React, { PureComponent } from 'react'
import axios from "axios";
import {Link} from "react-router-dom"
class Home extends PureComponent {
constructor(props) {
super(props)
this.state = {
planets: [],
filteredPlanets: []
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(e){ // eslint-disable-next-line
let planetssearchlist = this.state.planets.filter(planet => {
if(planet.name){
if(planet.name.toLowerCase().includes(e.target.value.toLowerCase())){
return true
}
}
})
this.setState({
filteredPlanets: planetssearchlist
})
}
componentDidMount(){
axios({
method: "GET",
url: "https://swapi.dev/api/planets/"
})
.then(response => {
console.log(response.data.results)
let planetslist = response.data.results;
this.setState({planets: planetslist, filteredPlanets: planetslist})
})
.catch(error => {
console.log("You've made an error with the planets load charles: ",error)
})
}
render() {
return (
<div>
<h1>Star Wars Planets</h1>
<form>
<input placeholder="searchbar" type="text" onChange={this.handleChange}></input>
</form>
{
this.state.filteredPlanets.map((planet,i) => (
<Link to={{ pathname: "/info", state:{planet:planet} }}><p key={i}>{planet.name}</p></Link>
))
}
</div>
)
}
}
export default Home
PLANETDETAIL.JS
import React, { PureComponent } from "react";
import axios from "axios";
class PlanetDetail extends PureComponent {
constructor(props) {
super(props)
this.state = {
url: "",
planetInfo: {},
isGettingPlanetInfo: false
};
}
getPlanetInfo = () => {
this.setState({
isGettingPlanetInfo: true
});
axios({
method: "GET",
url: this.state.url
})
.then(response => {
console.log(response.data);
this.setState({
planetInfo: response.data,
isGettingPlanetInfo: false
});
})
.catch(error => {
this.setState({
isGettingPlanetInfo: false
});
console.log(
"You've made an error with the planet detail load charles: ",error
);
});
};
componentDidMount = () => {
this.setState(
{
url: this.props.location.state.planet.url
},
this.getPlanetInfo
);
};
render() {
return (
<div>
<div>
{this.state.isGettingPlanetInfo ?
(<p>Getting planet info...</p>) :
typeof this.state.planetInfo === "object" && Object.keys(this.state.planetInfo).length ?
(
<div>
<h1>Planet details</h1>
<p>planet name: {this.state.planetInfo.name}</p>
<p>rotation period: {this.state.planetInfo.rotation_period}</p>
<p>orbital period: {this.state.planetInfo.orbital_period}</p>
<p>diameter: {this.state.planetInfo.diameter}</p>
<p>climate: {this.state.planetInfo.climate}</p>
<p>gravity: {this.state.planetInfo.gravity}</p>
<p>terrain: {this.state.planetInfo.terrain}</p>
<p>surface water: {this.state.planetInfo.surface_water}</p>
<p>population: {this.state.planetInfo.population}</p>
</div>
) : (
""
)}
</div>
<div>
<h1>Notable people</h1>
{
this.state.planetInfo.residents.map(resident =>
<p>{resident.url}</p>
)
}
</div>
</div>
);
}
}
export default PlanetDetail
Since you're using state to set the url to fetch for planet details, planetInfo won't be populated in your initial render. You're already guarding against this for the other planet info, so do the same for the residents section by putting it under the same conditional.
You need to actually fetch the resident details – they aren't part of the planet response. I'd abstract that into a new component. Here's a Codepen of such a component.
render() {
return (
<div>
<div>
{this.state.isGettingPlanetInfo ? (
<p>Getting planet info...</p>
) : typeof this.state.planetInfo === "object" &&
Object.keys(this.state.planetInfo).length ? (
<>
<div>
<h1>Planet details</h1>
<p>planet name: {this.state.planetInfo.name}</p>
<p>rotation period: {this.state.planetInfo.rotation_period}</p>
<p>orbital period: {this.state.planetInfo.orbital_period}</p>
<p>diameter: {this.state.planetInfo.diameter}</p>
<p>climate: {this.state.planetInfo.climate}</p>
<p>gravity: {this.state.planetInfo.gravity}</p>
<p>terrain: {this.state.planetInfo.terrain}</p>
<p>surface water: {this.state.planetInfo.surface_water}</p>
<p>population: {this.state.planetInfo.population}</p>
</div>
<div>
<h1>Notable people</h1>;
{
this.state.planetInfo.residents.map(<Resident url={resident} />);
}
</div>
</>
) : (
""
)}
</div>
</div>
);
}
// Resident.js
const Resident = ({ url }) => {
const [resident, setResident] = React.useState(null);
React.useEffect(() => {
const fetchResident = async () => {
try {
const response = await axios({
method: "GET",
url
});
setResident(response.data);
} catch (error) {
console.error("Error retrieving resident", error);
}
};
fetchResident();
}, [url]);
return resident ? (
<div>
<span>{resident.name}</span>
</div>
) : (
<div>Fetching resident...</div>
);
};
The parent component Dashboard holds the state for every ListItem I add to my Watchlist. Unfortunately, every time I am adding an Item, it gets added to the DB, but only shows up when I refresh the browser.
class UserDashboard extends React.Component {
state = {
data: []
}
componentWillMount() {
authService.checkAuthentication(this.props);
}
isLoggedIn = () => {
return authService.authenticated()
}
getAllCoins = () => {
//fetches from backend API
}
addWishlist = () => {
this.getAllCoins()
.then(things => {
this.setState({
data: things
})
})
console.log("CHILD WAS CLICKED")
}
componentDidMount() {
this.getAllCoins()
.then(things => {
this.setState({
data: things
})
})
}
render() {
return (
<div className="dashboard">
<h1>HI, WELCOME TO USER DASHBOARD</h1>
<SearchBar
addWishlist={this.addWishlist}
/>
<UserWatchlist
data={this.state.data}
/>
</div>
);
}
}
The User Watchlist:
class UserWatchlist extends React.Component {
constructor(props) {
super(props)
}
// componentDidUpdate(prevProps) {
// if (this.props.data !== prevProps.data) {
// console.log("CURRENT", this.props.data)
// console.log("PREVs", prevProps.data)
// }
// }
render() {
return (
<div>
<h2>These are tssssyou are watching:</h2>
<ul className="coin-watchlist">
{
this.props.data.map((coin, idx) => {
return <ListItem key={idx}
coin={coin.ticker}
price={coin.price}
/>
})
}
</ul>
</div>
)
}
}
The search Bar that shows potential Items to watch over:
class SearchBar extends React.Component {
constructor(props) {
super(props)
this.state = {
coins: [],
searchValue: ""
}
}
searchHandler = e => {
e.preventDefault()
const value = e.target.value
this.setState({
searchValue: value
});
if (value === "") {
this.setState({
coins: []
})
} else {
this.getInfo()
}
}
getInfo = () => {
// Searches the API
}
addWishlist = () => {
this.props.addWishlist();
}
render() {
const {coins, searchValue} = this.state
return (
<div className="coin-search">
<form>
<input
type="text"
className="prompt"
placeholder="Search by ticker symbol"
value={searchValue}
onChange={this.searchHandler}
/>
</form>
<ul className="search-suggestions">
{
coins.filter(searchingFor(searchValue)).map( coin =>
<Currency
coin={coin}
addWishlist={this.addWishlist}
/>
)
}
</ul>
</div>
);
}
}
And the actual Currency that gets clicked to be added:
class Currency extends React.Component {
addToWatchlist = () => {
// POST to backend DB to save
};
fetch("/api/add-coin", settings)
.catch(err => {
return err
})
}
clickHandler = () => {
this.addToWatchlist()
this.props.addWishlist()
}
render() {
return(
<div className="search-results">
<li>
<h3> { this.props.coin.currency } </h3>
<button
className="add-to-list"
onClick={this.clickHandler}
>
+ to Watchlist
</button>
</li>
</div>
)
}
}
As you can see, I am sending props down all the way down to child. When I click the button to Add to Watchlist, I see the console.log message appear, saying "CHILD WAS CLICKED". I've even tried just calling the method to fetch from backend API again.
Also, in UserWatchlist, I've tried a componentDidUpdate, but both prevProps and this.props show the very same array of data. Somewhere in the chain, my data is getting lost.
This is also my first time posting a question here, so if it can be improved, I am happy to add extra details and contribute something to this community
You probably forgot to wait for addToWatchlist to complete:
addToWatchlist = () => {
// POST to backend DB to save
return fetch("/api/add-coin", settings)
.catch(err => {
return err
})
}
clickHandler = () => {
this.addToWatchlist().then(() => {
this.props.addWishlist()
})
}
I have a React component which gets from an API data with fetch of 10 images. I would like to use infinite scroll to load more sets of 10 images.
What I made to do is to listen the event of reaching the bottom of the website and posting new url of nest 10 images in console only :)
Should I focus on getting all data in my url, or focus on render and usage of related function?
Or maybe the problem is because I get data in componentDidMount and I don't know how to update whole state?
import React from 'react';
import ReactDOM from 'react-dom';
class ViewSection extends React.Component {
constructor(props) {
super(props);
this.state = {
image: [],
like: [],
location: [],
first_name: [],
last_name: [],
pictureId: [0,1,2,3,4,5,6,7,8,9],
page: 1
};
this.handleScroll = this.handleScroll.bind(this) // I'M MOVING DATA TO HANDLE SCROLL
};
handleScroll(e) {
e.preventDefault();
let documentHeight = document.documentElement.offsetHeight;
let windowHeight = window.innerHeight;
let windowScroll = window.scrollY;
let scrollTotal = windowScroll + windowHeight;
if (scrollTotal == documentHeight) {
this.setState({ page: this.state.page + 1 })
// console.log(this.state.page);
}
};
componentDidMount() {
let urlImage = ('https://api.website.com/categories/' + this.props.params.sectionId + '/photos/?client_id=MYID&page=' + this.state.page); // MAP ALL PAGES?????
window.addEventListener("scroll", this.handleScroll,false);
fetch(urlImage)
.then(resp => resp.json())
.then(response => {
// console.log(response);
// console.log(this.state.page);
let arrayOfImages = response.map((item) => item.urls.small );
let arrayOfLikes = response.map((item) => item.likes );
let arrayOfLoc = response.map((item) => item.user.location );
let arrayOfFirst_Names = response.map((item) => item.user.first_name );
let arrayOfLast_Names = response.map((item) => item.user.last_name );
this.setState ({
image : arrayOfImages,
like : arrayOfLikes,
location : arrayOfLoc,
first_name : arrayOfFirst_Names,
last_name : arrayOfLast_Names
})
});
};
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll,false);
};
render() {
// console.log(this.state.image);
console.log(this.state.page); // LISTENS AND RENDERS ALL CHANGES... MAYBE PROMISE.ALL ON urlImage...?
let section = this.state.image.map((elem, i, page) => {
return (
<Link key={i} onScroll={this.handleScroll} className="section-picture" to= {`/section/${this.props.params.sectionId}/picture/${this.state.pictureId[i]}`}>
<img className="image" src={elem} alt="" />
<div className="section-picture-stats">
<div className="section-picture-stat"> author: {this.state.first_name[i]} {this.state.last_name[i]}</div>
<div className="section-picture-stat">{this.state.like[i]} like(s)</div>
<div className="section-picture-stat">{this.state.location[i]}
</div>
</div>
</Link>
)
});
return (
<div className="gallery">
<h1>Section</h1>
<div className="buttons">
<div className="sort-clicks click">sort by: <a className="click" href="">new</a> or <a className="click" href="#">trending</a></div> <Link className="click" to='/'>back</Link>
</div>
<div className="section-picture-list">{section}</div>
</div>
)
};
};
export { ViewSection }
It looks to me like the value of section will be the result of mapping an empty array, since this.state.image will be empty until the fetch in componentDidMount finishes. Try adding a check in your render function like so:
let section;
if (this.state.images.length === 0) section = <p>Loading...</p>;
else section = this.state.image.map((elem, i, page) => {
...your code goes here...
});
This way it should update properly at least on the initial render