我收到此错误
img.user.username未定义。
<Typography variant="h6" align="center">{img.user.username}</Typography>
上传图片时
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) => {
const newImage = {...response.data}
//update component-state
this.setState({
image_url: newImage.img_url,
description: '',
images: [
{
id: newImage.id,
// user: newImage.user.username,
image_title: newImage.image_title,
img_url: newImage.img_url,
created_at: new Date().toLocaleString().replace(',', ''),
updated_at: new Date().toLocaleString().replace(',', '')
},
...this.state.images,
],
})
});
}
{...response.data}
不包含属性user
,所以可以理解此错误,但是当我刷新页面时
img.user.username
显示用户名。,并且没有错误。
这会从后端获取帖子,我如何才能使其同时工作,例如何时上传图片以及何时刷新页面。
如果他们是编辑问题标题的更好方法,请做出更好的问题标题。
componentWillMount(){
Axios.get('/images/uploads').then( (response) => {
// let img;
// let imgTitle;
Object.keys(response.data).forEach( (key) => {
console.log(response.data[key]);
this.setState({
images:[ ...this.state.images, response.data[key]]
})
console.log(this.state.images);
});
})
}
完整代码
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 Divider from '@material-ui/core/Divider';
import Axios from '../Axios';
import Image from './Image';
import moment from 'moment';
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) => {
const newImage = {...response.data}
//update component-state
this.setState({
image_url: newImage.img_url,
description: '',
images: [
{
id: newImage.id,
// user: newImage.user.username,
image_title: newImage.image_title,
img_url: newImage.img_url,
created_at: new Date().toLocaleString().replace(',', ''),
updated_at: new Date().toLocaleString().replace(',', '')
},
...this.state.images,
],
})
});
}
handleChange = (e) => {
// e.preventDefault();
this.setState({
[e.target.name]: e.target.value
})
// console.log(this.state.description)
}
componentWillMount(){
Axios.get('/images/uploads').then( (response) => {
// let img;
// let imgTitle;
Object.keys(response.data).forEach( (key) => {
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.images.length !== prevState.images.length) {
console.log(this.state.images);
}
// debugger;
}
onUploadClick = (e) => {
e.preventDefault();
this.setState({
upload: !this.state.upload
})
}
deleteImg = (id) => {
Axios.post(`/images/delete/${id}`).then( () => {
this.setState({
images: [ ...this.state.images.filter(img => img.id !== id)]
})
})
}
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>
<br></br>
<br></br>
{this.state.upload ? (
<div>
<TextField
id="outlined-name"
label="Image Title"
name="description"
type="text"
required={true}
fullWidth
style={{ borderRadius: '0px'}}
className=""
value={this.state.description}
onChange={this.handleChange}
margin="normal"
/>
<br></br>
<br></br>
{/* so here what we are saying, if this text field is FILLED show the uploader component
else hide it.
*/}
{this.state.description ? uploader : null}
</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 style={{padding:'20px 20px'}}>
{/* // empty image_title */}
<Typography style={{ padding: '30px 5px', letterSpacing:'8px', textTransform:'uppercase'}} variant="h4" align="center">{img.image_title}</Typography>
<Divider style={{ width: '150px', margin:'10px auto', backgroundColor:'#000000'}} variant="middle" />
<Image image_url={img.img_url} />
<Typography variant="h6" align="center">{img.user.username}</Typography>
<Typography variant="h6" align="center">{moment(img.created_at).calendar()}</Typography>
<Button onClick={() => this.deleteImg(img.id)} variant="outlined" component="span" color="primary">
Delete
</Button>
</Paper>
</Grid>
))
) : (
<div>
<Grid item md={8}>
<Typography>No Images yet</Typography>
</Grid>
</div>
)}
</Grid>
{/* Images */}
</Grid>
</div>
)
}
}
export default Dashboard;
后端
router.get('/uploads', async (req, res) => {
await Image.query( (image) => {
image.orderBy('img_url', 'DESC')
image.limit(10)
// if you want to include the user with the image, you would use the withRelated
}).fetchAll({withRelated: ['user']}).then( (images) => {
// console.log(images.toJSON());
return res.status(200).json(images.toJSON());
})
})
router.post('/upload', multipartMiddleware, upload.single('ourImage'), (req, res) => {
if(!req.files){
return res.status(500).send("Please upload a file");
}
// console.log(req.files)
cloud.uploader.upload(req.files.ourImage.path, {crop: "fill", folder: '/uploads'} , (err, result) => {
if(err){
return res.status(500).send(err);
}
// console.log(req.user)
const img = new Image({
img_url:result.url,
image_title:req.files.ourImage.name,
user_id: req.user.id
});
// console.log(img);
img.save().then( img => {
return res.status(200).json(img);
});
});
});
答案 0 :(得分:1)
您的实际问题是您的后端方法GET /images/uploads
和POST /images/upload
的每个项目都不具有相同的属性,即,没有提供user
来响应POST {{1 }},从而导致前端数据不一致。
最佳解决方案:在POST /images/update
的响应中添加user
,确保两者的响应数据相同
如果无法这样做:
images/upload
。这是昂贵的,但是要确保数据中的所有项目都具有渲染所需的属性。Axios.get('/images/uploads')
但是,这不会保持列表的完整性。
<Typography variant="h6" align="center">{!!img.user ? img.user.username : ""}</Typography>
,因为上传者将始终是当前用户。
user
答案 1 :(得分:1)
首先,确保后端POST响应以与GET响应相同的格式返回数据。
const img = new Image({
img_url:result.url,
image_title:req.files.ourImage.name,
user_id: req.user.id
});
Image.save().then(img => {
/* Do something like this, but for a single image...
await Image.query(image => {
image.orderBy("img_url", "DESC");
image.limit(10);
})
.fetchAll({ withRelated: ["user"] })
.then(images => {
return res.status(200).json(images);
});
*/
});
此外,当您在handleUpload方法中调用setState时,您没有在要添加到images数组的新对象上设置user.username
属性。也许尝试更换
this.setState({
images: [
{
id: newImage.id,
// user: newImage.user.username,
image_title: newImage.image_title,
img_url: newImage.img_url,
created_at: new Date().toLocaleString().replace(',', ''),
updated_at: new Date().toLocaleString().replace(',', '')
},
...this.state.images
]
})
使用
this.setState({
images: [
{
id: newImage.id,
user: {
username: newImage.user.username
},
image_title: newImage.image_title,
img_url: newImage.img_url,
created_at: new Date().toLocaleString().replace(',', ''),
updated_at: new Date().toLocaleString().replace(',', '')
},
...this.state.images
]
})
或进一步简化
this.setState(prevState => ({
images: [
{
...newImage,
created_at: new Date().toLocaleString().replace(',', ''),
updated_at: new Date().toLocaleString().replace(',', '')
},
...prevState.images
]
}))
答案 2 :(得分:0)
因此,在从@blaz和@Gabor Szekely获得反馈后,我重构了代码,现在它可以工作了。
我意识到在提出帖子请求时需要使用[0]
,然后在上传帖子时显示该帖子。像这样
this.setState({
description:'', // resets title after upload
images: [
{
id: newImage[0].id,
user:{
username: newImage[0].user.username
},
image_title: newImage[0].image_title,
img_url: newImage[0].img_url,
created_at: new Date().toLocaleString().replace(',', ''),
updated_at: new Date().toLocaleString().replace(',', '')
},
...this.state.images
]
})
以及在POST
方法中查询图像。我的印象是我无法在发帖请求中获取图像,但是您当然可以。以及将您从router.post
到router.all
的路线。
但是,我想将其保留为帖子,因为它无论哪种方式都可以工作。
router.post('/upload', multipartMiddleware, upload.single('ourImage'), (req, res) => {
if(!req.files){
return res.status(500).send("Please upload a file");
}
// console.log(req.files)
cloud.uploader.upload(req.files.ourImage.path, {crop: "fill", folder: '/uploads'} , (err, result) => {
if(err){
return res.status(500).send(err);
}
// console.log(req.user)
const img = new Image({
img_url:result.url,
image_title:req.files.ourImage.name,
user_id: req.user.id
});
// console.log(img);
// fetches image with user when a upload is made
img.save().then( async () => {
await Image.query(image => {
image.orderBy("img_url", "DESC");
image.limit(10);
})
.fetchAll({ withRelated: ["user"] })
.then(images => {
return res.status(200).json(images);
});
});
});
});