Issue with react state not updating/incrementing - javascript

I'm trying to do pagination by clicking on some text that calls a method to increment the state value. The state value then gets passed to the axios call which should then call the next page. I'm noticing however that while the state is getting increment in a console.log from the render function, the axios call is not getting called again with the new state value. Anyone have any idea how I can fix this?
constructor(props) {
super(props);
this.state = {
people: [],
planets: [],
page: 1
};
this.pageIncrementer = this.pageIncrementer.bind(this);
}
componentWillMount() {
let page = this.state.page;
axios({
method: 'GET',
url: `http://localhost:3008/people?_page=${page}&_limit=10`
}).then((response) => {
this.setState({
people: response
});
}).catch((error) => {
console.log('There is an error in the Card axios call for people: ', error);
})
axios({
method: 'GET',
url: `http://localhost:3008/planets?_page=${page}&_limit=10`
}).then((response) => {
this.setState({
planets: response
});
}).catch((error) => {
console.log('There is an error in the Card axios call for planets: ', error);
})
}
pageIncrementer() {
this.setState({
page: this.state.page + 1
});
}

componentWillMount called only once, you need componentDidUpdate
https://facebook.github.io/react/docs/react-component.html#componentdidupdate
let getData = () => Math.random();
class Example extends React.Component{
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this)
this.state = {
name: ''
};
}
componentWillMount(){
console.log('componentWillMount')
}
componentDidUpdate(){
console.log('componentDidUpdate')
}
handleChange(e) {
this.setState({
name: this.props.getData()
});
}
render() {
return <div className="widget">
{this.state.name}
<button onClick={this.handleChange}>Inc</button>
</div>;
}
}
React.render(<Example getData={getData}/>, document.getElementById('container'));
Edit(alternative way):
let getData = () => Math.random();
class Example extends React.Component{
constructor(props) {
super(props);
this.makeRequest = this.makeRequest.bind(this)
this.state = {
page:1,
name:''
};
}
makeRequest(next){
fetch('https://jsonplaceholder.typicode.com/posts/'+this.state.page)
.then(
result => {
console.log('do')
return result.json()}
)
.then(
(resp) => this.setState({
name:resp, page:this.state.page+1})
)
}
render() {
return <div className="widget">
{this.state.name}
<button onClick={this.makeRequest}>Request</button>
</div>;
}
}
React.render(<Example getData={getData}/>, document.getElementById('container'));

Related

Reac js TypeError: this.state.data.map is not a function

class CardList extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
}
componentDidMount() {
firestore
.collection('users')
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.setState({ data: doc.data() });
});
});
}
render() {
return (
<div className="cardlist">
{this.state.data.email
? this.state.data.map((data) => {
return <div>{this.state.data.email}</div>;
})
: console.log('error')}
</div>
);
}
}
TypeError: this.state.data.map is not a function
I want to take out the emails in the Firestore and print them out, but I can't print them because of typeerror. Why is there an error?
console.log(this.state.data) result is
{ createdAt: t, name: 'good', email: 'good#gmail.com', isTutor: 'off' };
{ name: 'joe', isTutor: 'on', email: 'joe#gmail.com', createdAt: t };
You are not assigning value to your array properly, rather you should do like the code below. I've also refactored the code in render function.
class CardList extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
}
componentDidMount() {
firestore
.collection('users')
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.setState({ data: [...this.state.data, doc.data()] });
});
});
}
render() {
return (
<div className="cardlist">
{this.state.data &&
this.state.data.map((item) => {
return <div>{item.email}</div>;
})}
</div>
);
}
}
this.setState({ data: doc.data() }); - you are not adding to the state, but replacing it with an object for each doc. And you cannot .map an object, thus the error.

Pass props to another component and redraw the page

In 1 component, when I click on the picture, I get its id, which I pass to another component via props. I need to receive these props every time and send a feth - request with the id of the image and then redraw the component. How to do it correctly?
first component
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
isOpen: false,
images: [],
idImg: ''
};
}
openModal = (e) => {
this.setState({ isOpen: true, idImg: e.target.id });
}
render() {
const {error, isLoaded, images} = this.state;
if (error) {
return <p>Error</p>
} else if (!isLoaded) {
return <p> Loading ... </p>
} else {
return (
<div className="row align-items-center m-4" onChange={this.onSelect}>
<Modal
isOpen={this.state.isOpen}
onCancel={this.handleCancel}
onSubmit={this.handleSubmit}
idImg={this.state.idImg}
></Modal>
{images.map(item => (
<div key={item.image_id} className="col-lg-4 col-lg-4 sm-1 p-2" style={{Style}} >
<img id={item.image_id} src={item.src} alt={item.src} onClick={this.openModal}></img>
</div>
))}
</div>
)
}
}
2 component:
export default class Modal extends Component {
constructor(props){
super(props);
this.state = {
imgSrc: ' ',
commentList: [],
_id: this.props.idImg
}
}
componentDidMount(){
fetch(`./api/${this.state._id}`, {
method: 'GET',
})
.then(res => res.json())
.then((result) => {
this.setState({
isLoaded: true,
imgSrc: result.src
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
);
Factor out the fetch into a utility function that can be called in componentDidMount and componentDidUpdate when the props update.
Also, don't store passed props into local component state, this is an anti-pattern in react. You can simply consume the passed idImg prop in the lifecycle methods.
export default class Modal extends Component {
constructor(props){
super(props);
this.state = {
imgSrc: ' ',
commentList: [],
}
}
fetchImage = imageId => {
this.setState({ isLoaded: false }); // <-- set loading state
fetch(`./api/${imageId}`, {
method: 'GET',
})
.then(res => res.json())
.then((result) => {
this.setState({
isLoaded: true,
imgSrc: result.src
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
);
};
componentDidMount() {
this.fetchImage(this.props.idImg); // <-- pass idImg prop
}
componentDidUpdate(prevProps) {
if (prevProps.idImg !== this.props.idImg) { // <-- compare idImg values
this.fetchImage(this.props.idImg); // <-- pass idImg prop
}
}
export default class Modal extends Component {
constructor(props){
super(props);
this.state = {
imgSrc: ' ',
commentList: [],
_id: this.props.idImg
}
this.nameFunction=this.nameFunction.bind(this);
}
componentDidMount(){
this.nameFunction();
}
componentDidUpdate(prevProps) {
if (prevProps.idImg!== this.props.idImg) {
this.setState({
_id: this.props.idImg,
})
}
}
nameFunction(){
fetch(`./api/${this.state._id}`, {
method: 'GET',
})
.then(res => res.json())
.then((result) => {
this.setState({
isLoaded: true,
imgSrc: result.src
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
);
}

React: calling a function in the onClick returns undefined

I'm trying to write a code that shows a list of images, etc. based on this answer:
https://stackoverflow.com/a/57635373/9478434
My code is:
class ImageGallery extends Component {
constructor(props) {
super(props);
this.state = {
photoIndex: 0,
isOpen: false,
imageList: [],
};
}
getImages() {
axios
.get(IMAGE_LIST_URL, {})
.then((response) => {
const data = response.data;
console.log(data);
this.setState({ imageList: response.data });
})
.catch((error) => {
setTimeout(() => {
console.log(error.response.data.message);
}, 200);
});
}
componentDidMount() {
this.getImages();
}
changePhotoIndex(imgIndex) {
this.setState({ photoIndex: imgIndex, isOpen: true });
}
render() {
const { photoIndex, isOpen, imageList } = this.state;
const singleImage = imageList.map(function (img, imgIndex) {
const imagePath = `http://localhost:8000/media/${img.filePath}`;
console.log(imagePath);
return (
<figure className="col-xl-3 col-sm-6">
<img
src={imagePath}
alt="Gallery"
className="img-thumbnail"
onClick={() => this.changePhotoIndex(imgIndex)}
/>
</figure>
);
});
return <div>{singleImage}</div>;
}
}
However while clicking on the image, I get a type error (t is undefined) in the console regarding to the line onClick={() => this.changePhotoIndex(imgIndex) and the state of app does not change.
The changePhoneIndex function considers itself and not the component as this.
You can bind it to the component itself and be able to access setState by adding this to the constructor:
constructor(props) {
super(props);
this.state = {
photoIndex: 0,
isOpen: false,
imageList: [],
};
this.changePhotoIndex.bind(this);
}
Or you can call set state directly:
onClick={() => this.setState({ photoIndex: imgIndex, isOpen: true })}
You forgot to bind your function. Add the following to your code:
constructor(props) {
super(props);
this.state = {
photoIndex: 0,
isOpen: false,
imageList: [],
};
this.changePhotoIndex = this.changePhotoIndex.bind(this); // missing
}

React Expected an assignment or function call and instead saw an expression

I'm trying to render the data from my database get this instead Failed to compile.
./src/components/list-pets.component.js
Line 38:5: Expected an assignment or function call and instead saw an expression no-unused-expressions
Search for the keywords to learn more about each error.enter code here
Here is my code from the trouble component
import React, { Component } from 'react';
import axios from 'axios';
export default class ListPets extends Component {
constructor(props) {
super(props);
this.state = {
pets: []
};
}
componentDidMount = () => {
this.getPets();
};
getPets = () => {
axios.get('http://localhost:5000/pets')
.then((response) => {
const data = response.data;
this.setState({ pets: data });
console.log('Data has been received!');
})
.catch((err) => {
console.log(err);
});
}
displayPet = (pets) => {
if (!pets.length) return null;
return pets.map((pet, index) => {
<div key={index}>
<h3>{pet.name}</h3>
<p>{pet.species}</p>
</div>
});
};
render() {
console.log('State: ', this.state);
return (
<div className='adopt'>
{this.displayPet(this.state.pets)}
</div>
)
}
}
You need to return a value at each pets.map iteration, currently you’re returning undefined.
return pets.map((pet, index) => {
return (
<div key={index}>
<h3>{pet.name}</h3>
<p>{pet.species}</p>
</div>
)
});
You have to wait until fetching data is completed.
You should have to define the loading bar while fetching.
class App extends Component {
constructor() {
super();
this.state = {
pageData: {},
loading: true
}
this.getData();
}
async getData(){
const res = await fetch('/pageData.json');
const data = await res.json();
return this.setState({
pageData: data,
loading: false
});
}
componentDidMount() {
this.getData();
}
render() {
const { loading, pageData } = this.state;
if (loading){
return <LoadingBar />
}
return (
<div className="App">
<Navbar />
</div>
);
}
}

React-Flux Load initial state

I'm trying to make an Ajax request in al React Flux app with axios and I get data after state is set.
I have this code in root app:
InitialData.getInitialPosts();
The API request it looks like this:
let PostsApi = {
getAllPosts(){
return axios.get('https://jsonplaceholder.typicode.com/posts')
.then( (response) => {
console.log('All posts: ', response.data)
return response.data;
});
}
}
export default PostsApi;
In actions/initialData.js i have this:
let LoadInitialData = {
getInitialPosts(){
Dispatcher.dispatch({
actionType: 'LOAD_INITIAL_POSTS',
initialPosts: {
posts: PostsApi.getAllPosts()
}
})
}
}
export default LoadInitialData;
In store:
let _posts = [];
const PostsStore = Object.assign({}, EventEmitter.prototype, {
addChangeListener(callback){
this.on('change', callback)
},
removeChangeListener(callback){
this.removeChangeListener('change', callback)
},
emitChange(callback){
this.emit('change', callback)
},
getAllPosts(){
return _posts;
}
});
Dispatcher.register(function(action){
switch(action.actionType){
case 'LOAD_INITIAL_POSTS':
_posts = action.initialPosts.posts;
PostsStore.emitChange();
break;
default:
}
});
In View:
export default class PostsPage extends React.Component {
constructor(){
super();
this.state = {
posts: []
}
}
componentDidMount(){
this.setState({
posts: PostsStore.getAllPosts()
});
}
render(){
const { posts } = this.state;
return(
<div>
{posts.map( post => {
return <h3 key={post.id}>{post.title}</h3>
})}
</div>
)
}
}
On console.log:
state: Object {posts: Array[0]}
state: Object {posts: Promise}
postsApi.js:7 All posts: [Object, Object, Object, Object, Object, Object...]
And the problem is the ajax request is after componentDidMount.
Your PostsPage component is not set up correctly to listen to changes from the store. The code you have will only grab the list of posts once when it first mounts. You want it to update whenever the Store gets new data.
To accomplish this, you need to utilize the add/remove Change Listener functions that you setup in the Store. It should look something like this;
export default class PostsPage extends React.Component {
constructor(){
super();
this.state = {
posts: []
}
}
_calculateState(){
this.setState({
posts: PostsStore.getAllPosts()
});
}
componentDidMount(){
PostsStore.addChangeListener(this._calculateState);
},
componentWillUnmount(){
PostsStore.removeChangeListener(this._calculateState);
},
render(){
const { posts } = this.state.posts;
return(
<div>
{posts.map( post => {
return <h3 key={post.id}>{post.title}</h3>
})}
</div>
)
}
}

Categories

Resources