首页 > 解决方案 > react 在状态更改时不获取api属性,而是在重新渲染时获取?

问题描述

我收到此错误

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);
        });

    });
});

标签: reactjs

解决方案


首先,确保后端 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 时,您不会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
  ]
}))

推荐阅读