Reputation: 1372
I'm trying to fetch the new updated state object within componentDidUpdate method. but its returning the the old values in the array.
A user enters a image title, and uploads an image, and react should console.log
a new array object with the new values. Without having to refresh.
For example the following array should have two keys along with values within the array considering i just added an image without refreshing.
and on refresh, the array is updated
dashboard.js 66
this is console.log(prevState.images)
[
{
"id": 139,
"image_title": "owl",
"img_url": "http://res.cloudinary.com/dq281hpqd/image/upload/v1559966278/uploads/wph0kuhzg52lxkhxmxsi.png",
"created_at": "2019-06-08T03:57:58.351Z",
"updated_at": "2019-06-08T03:57:58.351Z",
"user_id": null
},
{
"id": 138,
"image_title": "uuuuuu",
"img_url": "http://res.cloudinary.com/dq281hpqd/image/upload/v1559965905/uploads/m1oxtyae2d6pyvteidyq.png",
"created_at": "2019-06-08T03:51:46.815Z",
"updated_at": "2019-06-08T03:51:46.815Z",
"user_id": null
}
]
Ideally a new object array with the new values should append the array without having to refresh the page.
Dashboard.js
import React, { Component } from "react";
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import ImageUploader from 'react-images-upload';
import Axios from '../Axios';
import Image from './Image';
class Dashboard extends Component{
constructor(props){
super(props);
this.state = {
image_url: 'http://www.conservewildlifenj.org/images/artmax_1001.jpg',
images: [],
description:'',
upload:false,
}
}
handleUpload = file => {
const data = new FormData()
const image = file[0]
// console.log(this.state.description)
// data.append('ourImage', this.state.description)
data.append('ourImage',image, this.state.description )
Axios.post('/images/upload', data).then((response) => {
this.setState({
image_url:response.data.img_url,
description:response.data.image_title
// images: [...this.state.images, this.state.image_url ]
})
});
this.props.history.push("/dashboard");
}
handleChange = (e) => {
// e.preventDefault();
this.setState({
[e.target.name]: e.target.value
})
// console.log(this.state.description)
}
fileOnchange = (file) => {
this.setState({
[file[0].target.name]: file[0].target.value
})
}
componentWillMount(){
Axios.get('/images/uploads').then( (response) => {
// let img;
// let imgTitle;
Object.keys(response.data).forEach( (key) => {
// console.log(response.data);
// img = response.data[key].img_url
// imgTitle = response.data[key].image_title
// pulling in the data from the backend
console.log(response.data[key]);
this.setState({
images:[ ...this.state.images, response.data[key]]
})
console.log(this.state.images);
});
})
}
componentDidUpdate(prevProps, prevState) {
if (this.state.image_url !== prevState.image_url) {
// trying to fetch the new upated array. but not sure how.
console.log(prevState.images);
this.setState({
images: [this.state.image_url, this.state.description, ...this.state.images]
});
}
}
onUploadClick = (e) => {
e.preventDefault();
this.setState({
upload: !this.state.upload
})
}
render(){
const uploader = (
<ImageUploader
withIcon={true}
withPreview={true}
onChange={this.handleUpload}
singleImage={true}
buttonText='Upload an image'
imgExtension={['.jpg', '.gif', '.png', '.gif']}
maxFileSize={5242880}
/>
)
return(
<div>
<Grid container justify="center" spacing={16}>
<Grid item sm={8} md={6} style={{ margin: '40px 0px', padding: '0px 30px'}}>
<Typography align="center" variant="h6">
Welcome to the Dashboard
</Typography>
<Button onClick={this.onUploadClick} variant="outlined" component="span" color="primary">
{/* toggle between Upload or Close
Will be upload by default, else if upload is clicked, close will show.
*/}
{!this.state.upload ? "Upload": "Close"}
</Button>
{this.state.upload ? (
<div>
<TextField
id="outlined-name"
label="Image Title"
name="description"
type="text"
fullWidth
style={{ borderRadius: '0px'}}
className=""
value={this.state.description}
onChange={this.handleChange}
margin="normal"
/>
<br></br>
<br></br>
{uploader}
</div>
):(
null
)}
{this.state.images.length > 0 ? (
this.state.images.map( (img, i) => (
<Grid item sm={12} md={12} key={i} style={{ margin: '30px 0px'}}>
<Paper>
{/* // empty image_title */}
<Typography variant="h6" align="center">{img.image_title}</Typography>
<Image image_url={img.img_url} />
</Paper>
</Grid>
))
) : (
<div>
<Grid item md={8}>
<Typography>No Images yet</Typography>
</Grid>
</div>
)}
</Grid>
{/* Images */}
</Grid>
</div>
)
}
}
export default Dashboard;
componentDidUpdate
console.log(this.state.images);
The values are passed, but not the way i would like
The last two values are not within the nested array. as the rest as the others.
[
{
"id": 142,
"image_title": "cheese",
"img_url": "http://res.cloudinary.com/dq281hpqd/image/upload/v1559973164/uploads/bvfotlfivr4yqsqhkvrw.png",
"created_at": "2019-06-08T05:52:45.203Z",
"updated_at": "2019-06-08T05:52:45.203Z",
"user_id": null
},
{
"id": 141,
"image_title": "owl",
"img_url": "http://res.cloudinary.com/dq281hpqd/image/upload/v1559972965/uploads/w9aup9r76q6hiktdjvyp.png",
"created_at": "2019-06-08T05:49:26.911Z",
"updated_at": "2019-06-08T05:49:26.911Z",
"user_id": null
},
{
"id": 140,
"image_title": "tea",
"img_url": "http://res.cloudinary.com/dq281hpqd/image/upload/v1559966806/uploads/y9q4upguo3hy6vhkk0va.png",
"created_at": "2019-06-08T04:06:47.217Z",
"updated_at": "2019-06-08T04:06:47.217Z",
"user_id": null
},
{
"id": 139,
"image_title": "owl",
"img_url": "http://res.cloudinary.com/dq281hpqd/image/upload/v1559966278/uploads/wph0kuhzg52lxkhxmxsi.png",
"created_at": "2019-06-08T03:57:58.351Z",
"updated_at": "2019-06-08T03:57:58.351Z",
"user_id": null
},
{
"id": 138,
"image_title": "uuuuuu",
"img_url": "http://res.cloudinary.com/dq281hpqd/image/upload/v1559965905/uploads/m1oxtyae2d6pyvteidyq.png",
"created_at": "2019-06-08T03:51:46.815Z",
"updated_at": "2019-06-08T03:51:46.815Z",
"user_id": null
},
"http://www.conservewildlifenj.org/images/artmax_1001.jpg",
"cheese2"
]
Upvotes: 0
Views: 1923
Reputation: 15698
When you execute handleUpload()
it looks like you just are not passing the new image object correctly to your component-state, I'll break this down into parts:
handleUpload = file => {
const data = new FormData()
const image = file[0]
data.append('ourImage',image, this.state.description )
Axios.post('/images/upload', data).then((response) => {
This is good practice so you do not accidentally mutate the original object. Although to be honest we probably do not need to do this because the item is stored in your database and any mutations here would not affect it
const newImage = {...response.data}
We want a new object that contains all key-value pairs of the old object. Since the image object only has one layer of data { key: string }
not { key: {} }
using the spread operator ...
is sufficient.
We do this because we never want to run into a scenario like this:
const newImage = response.data // { url: "https://boygeniusreport.files.wordpress.com/2015/06/funny-cat.jpg?quality=98&strip=all&w=782"}
newImage.url = "https://boygeniusreport.files.wordpress.com/2016/05/scared-surprised-cat-face.jpg?quality=98&strip=all&w=782"
Our expectation is that our code will only change the url in newImage, but we will find that response.data also been changed:
console.log(newImage.url) //"https://boygeniusreport.files.wordpress.com/2016/05/scared-surprised-cat-face.jpg?quality=98&strip=all&w=782"
console.log(response.data.url) //"https://boygeniusreport.files.wordpress.com/2016/05/scared-surprised-cat-face.jpg?quality=98&strip=all&w=782"
In JavaScript, when you make a copy of an object, const newImage = oldImage
you do not just simply copy its value. You copy its entire reference in memory. Making a shallow-copy with ...
works around that by making a completely new reference. It's good practice to make a shallow-copy if you ever want to define new values inside an object you initially copied.
this.setState({
image_url: newImage.img_url,
description: newImage.image_title,
images: [
...this.state.images,
{
id: newImage.id,
image_title: newImage.image_title,
img_url: newImage.img_url,
created_at: newImage.created_at,
updated_at: newImage.updated_at,
user_id: null
}
]
})
});
}
This code-block here says we will make a shallow-copy of this.state.images
, Then we will create a new object, using the values inside our newImage
obj. As you remember, newImage
was made by creating a shallow-copy of response.data. So newImage has all the key-value pairs we need.
images: [
...this.state.images,
{
id: newImage.id,
image_title: newImage.image_title,
img_url: newImage.img_url,
created_at: newImage.created_at,
updated_at: newImage.updated_at,
user_id: null
}
]
Also, when you call componentDidUpdate()
you are adding only description and image_url as individual values to the images array. This seems like a mistake, you already added the image in your handleClick()
. But, if really want tom you should pass in an object if anything like so.
componentDidUpdate(prevProps, prevState){
if(this.state.images.length !== prevState.images.length){
//unnecessary, since we already added the image, but test it out anyway.
this.setState({
images: [...this.state.images, { image_url: this.state.image_url, description: this.state.description}]
})
}
}
Note: If you decide to use componentDidUpdate()
you'll notice that you will not have fields like "created_at", "updated_at"
and others available because those are fields added to the new image object from your backend API.
Upvotes: 2