Home > Software design >  Not Able to post asynchronously in react-redux
Not Able to post asynchronously in react-redux

Time:12-18

I am basically new to react and just following a guided project from a YouTube channel. I am half way in this project. From app js Form component is called and in Form component when i click on submit it calls createPost() function in "client/src/actions/posts.js" and the data gets dispatched even though async and await keyword present in createPost() it is not posting asynchronisingly. It shows error in try block which is in createPost()

Cannot destructure property 'data' of '(intermediate value)' as it is undefined

but inside that createPost() another createPost() is called which is in "client/src/api/index.js" where axios is used to post the data. When i try to console log the response over there in browser console i am getting

status:201
statusText:"Created"

After this when i reloaded the data are posted and it is reflecting in the UI. I believe this is because of async and await keyword not working. In vscode the await keyword present in createPosst() which is in "client/src/actions/posts.js" gets underlined and shows 'await' has no effect on the type of this expression. (Please refer createPost() function in "client/src/actions/posts.js").I have attached the code files below . Thank you.

Client Folder Structure

    ▼ client  
      > node_modules  
      > public  
      ▼ src  
        ▼ actions
            posts.js
        ▼ api
            index.js
        ▼ components
            ▼ Form
              Form.js
              styles.js
            ▼ Posts
              ▼ Post
                Post.js
                styles.js
              Posts.js
              styles.js     
        ▼ images
            15.png
        ▼ reducers
            index.js
            posts.js
        App.js
        index.js
        styles.js
     .gitignore
      package.json
      yarn.lock

client/src/Index.js

    import React from "react";
    import ReactDOM from "react-dom";
    import { Provider } from "react-redux";
    import { createStore, applyMiddleware, compose } from "redux";
    import thunk from "redux-thunk";
    import {reducers} from "./reducers/index";
    import App from "./App.js";
    
    const store = createStore(reducers, compose(applyMiddleware(thunk)));
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
        document.getElementById('root')
      );

client/src/App.js

    import React, {useEffect} from "react";
    import {
      Container,
      AppBar,
      Typography,
      // Groe,
      Grid,
      Grow,
    } from "@material-ui/core";
    import {useDispatch} from 'react-redux';
    import {getPosts} from './actions/posts';
    import Posts from "./components/Posts/Posts.js";
    import Form from "./components/Form/Form.js";
    import useStyles from './styles';
    import img from "./images/15.png";
    
    export default function App() {
      const classes = useStyles();
      const dispatch = useDispatch();
      useEffect(()=>{
        dispatch(getPosts());
      },[dispatch])
      return (
        <Container maxwidth="lg">
          <AppBar className={classes.appBar} position="static" color="inherit">
            <Typography className={classes.heading} varient="h2" align="center">
              Memories
            </Typography>
            <img className={classes.image} src={img} alt="memories" height="60" />
          </AppBar>
          <Grow in>
            <Container>
              <Grid
                container
                justifyContent="space-between"
                alignItems="stretch"
                spacing={3}
              >
                <Grid item xs={12} sm={7}>
                  <Posts />
                </Grid>
                <Grid item xs={12} sm={4}>
                  <Form />
                </Grid>
              </Grid>
            </Container>
          </Grow>
        </Container>
      );
    }

client/src/actions/posts.js

    import * as api from '../api';
    export const getPosts = () => async(dispatch)=>{
        try{
            const {data} = await api.fetchPosts();
            dispatch({type:'FETCH_ALL',payload:data});
        }catch (error){
            console.log(error.message);
        }
    }
    
    export const createPost = (post) => async(dispatch) =>{
        try{
            //In vscode when i hoover over the below await keyword it shows like below
            //'await' has no effect on the type of this expression. 
            const {data}  =  await api.createPost(post)
            dispatch({type:'CREATE',payload:data})
            
        }catch (error){
            console.log(error.message)
        }
    }

client/src/api/index.js

    import axios from 'axios';
    
    const url = 'http://localhost:5000/posts';
    
    export const fetchPosts = () => axios.get(url);
    export const createPost = (newPost) => {
        axios.post(url,newPost).then((res)=>console.log(res))};

client/src/components/Form/Form.js

    import React, { useState} from "react";
    import { TextField, Button, Typography, Paper } from "@material-ui/core";
    import useStyles from "./styles";
    import FileBase from "react-file-base64";
    import { useDispatch } from "react-redux";
    import { createPost } from "../../actions/posts";
    
    export default function Form() {
      const classes = useStyles();
      const dispatch = useDispatch();
      const [postData, setPostData] = useState({
        creator: "",
        title: "",
        message: "",
        tags: "",
        selectedfile: "",
      });
      const handleSubmit = (e) => {
        e.preventDefault();
        dispatch(createPost(postData));
      };
      const clear = () => {};
      return (
        <Paper className={classes.paper}>
          <form
            autoComplete="off"
            noValidate
            className={`${classes.root} ${classes.form}`}
            onSubmit={handleSubmit}
          >
            <Typography variant="h6">creating a Memory</Typography>
            <TextField
              name="creator"
              variant="outlined"
              label="Creator"
              fullWidth
              value={postData.creator}
              onChange={(e) =>
                setPostData({ ...postData, creator: e.target.value })
              }
            />
            <TextField
              name="title"
              variant="outlined"
              label="Title"
              fullWidth
              value={postData.title}
              onChange={(e) => setPostData({ ...postData, title: e.target.value })}
            />
            <TextField
              name="message"
              variant="outlined"
              label="Message"
              fullWidth
              value={postData.message}
              onChange={(e) =>
                setPostData({ ...postData, message: e.target.value })
              }
            />
            <TextField
              name="tags"
              variant="outlined"
              label="Tags"
              fullWidth
              value={postData.tags}
              onChange={(e) => setPostData({ ...postData, tags: e.target.value })}
            />
            <div className={classes.fileInput}>
              <FileBase
                type="file"
                multiple={false}
                onDone={({ base64 }) =>
                  setPostData({ ...postData, selectedFile: base64 })
                }
              />
            </div>
            <Button
              className={classes.buttonSubmit}
              variant="contained"
              color="primary"
              size="large"
              type="submit"
              fullWidth
            >
              Submit
            </Button>
            <Button
              variant="contained"
              color="secondary"
              size="small"
              onClick={clear}
              fullWidth
            >
              clear
            </Button>
          </form>
        </Paper>
      );
    }

client/src/reducers/posts.js

    export default (posts=[],action) =>{
        switch (action.type){
            case 'FETCH_ALL':
                return action.payload;
            case 'CREATE':{
                return [...posts,action.payload];}
            default:
                return posts;
        }
    }

client/src/reducers/index.js

    import { combineReducers } from "redux";
    import posts from "./posts";
    
    export const reducers= combineReducers({
      posts:posts,
    });

Server Folder Structure

    ▼ server
      ▼ controllers
          posts.js
      ▼ models
          postMessage.js
      > node_modukes
      ▼ routes
          posts.js
      index.js
      package.json
      yarn.lock

server/controllers/posts.js

    import PostMessage from "../models/postMessage.js";
        
        export const getPosts = async (req, res) => {
          try {
            const postMessages = await PostMessage.find();
            res.status(200).json(postMessages);
          } catch (error) {
            res.status(400).json({ message: error.message });
          }
        };
        
        export const createPost = async (req, res) => {
          const post = req.body;
          const newPost = new PostMessage(post);
          try {
            await newPost.save();
            res.status(201).json(newPost);
          } catch {
            res.status(409).json({ message: error.message });
          }
        };

server/models/postMessage.js

    import mongoose from 'mongoose';
    
    const postSchema = mongoose.Schema({
        title: String,
        message: String,
        creator: String,
        tags: [String],
        selectedFile: String,
        likeCount: {
            type: Number,
            default: 0,
        },
        createdAt: {
            type: Date,
            default: new Date(),
        },
    })
    
    var PostMessage = mongoose.model('PostMessage', postSchema);
    
    export default PostMessage;

server/routes/posts.js

    import express from "express";
    import { getPosts, createPost } from "../controllers/posts.js";
    const router = express.Router();
    
    router.get("/", getPosts);
    router.post("/", createPost);
    
    export default router;

server/index.js

    import express from "express";
    import bodyParser from "body-parser";
    import mongoose from "mongoose";
    import cors from "cors";
    import postRoutes from "./routes/posts.js";
    
    const app = express();
    app.use(bodyParser.json({ limit: "30mb", extended: true }));
    app.use(bodyParser.urlencoded({ limit: "30mb", extended: true }));
    app.use(cors());
    app.use("/posts", postRoutes);
    
    const CONNECTION_URL ="...(connection url provided correctly in code)";
    const PORT = process.env.PORT || 5000;
    
    mongoose
      .connect(CONNECTION_URL, { useNewUrlParser: true, useUnifiedTopology: true })
      .then(() => app.listen(PORT, () => console.log(`Port:${PORT}`)))
      .catch((error) => console.log(error.message));

CodePudding user response:

In your client/src/api/index.js you didn't return the API response. You need to return the response like this

export const createPost = (newPost) => {
  return axios.post(url, newPost).then((res)=> res )
};

or

export const createPost = (newPost) => axios.post(url, newPost);
  • Related