无法在 react-redux 中异步发布

Posted

技术标签:

【中文标题】无法在 react-redux 中异步发布【英文标题】:Not Able to post asynchronously in react-redux 【发布时间】:2022-01-16 08:20:30 【问题描述】:

我基本上是刚接触 YouTube 频道的指导项目的新手。 我已经完成了这个项目的一半。 从应用程序 js 调用表单组件,当我单击提交时,它在表单组件中调用“client/src/actions/posts.js”中的createPost() 函数,即使createPost() 中存在 async 和 await 关键字,数据也会被调度它不是异步发布的。它在 createPost() 中的 try 块中显示错误

无法按原样解构“(中间值)”的属性“数据” 未定义

但是在 createPost() 内部调用了另一个 createPost(),它位于“client/src/api/index.js”中,其中 axios 用于发布数据。当我尝试在浏览器控制台中控制台记录响应时,我得到了

状态:201 statusText:"已创建"

在此之后,当我重新加载数据时,它会在 UI 中反映出来。我相信这是因为 async 和 await 关键字不起作用。在 vscode 中,“client/src/actions/posts.js”中的 createPosst() 中的 await 关键字被加下划线并显示 'await' has no effect on the type of this expression.(请参阅“client/src/actions/posts.js”中的 createPost() 函数) ).我附上了下面的代码文件。谢谢。

客户端文件夹结构

    ▼ 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 max>
          <AppBar className=classes.appBar position="static" color="inherit">
            <Typography className=classes.heading varient="h2" align="center">
              Memories
            </Typography>
            <img className=classes.image src=img   />
          </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
      ▼ controllers
          posts.js
      ▼ models
          postMessage.js
      > node_modukes
      ▼ routes
          posts.js
      index.js
      package.json
      yarn.lock

服务器/控制器/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 );
          
        ;

服务器/模型/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;

服务器/路由/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;

服务器/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));

【问题讨论】:

因为您的数据获得了空值,所以您没有返回响应。您需要在此处从 client/src/api/index.js 返回 API 响应 当我尝试返回 api 它显示无法读取控制台中未定义的属性你能用代码解释一下吗 你可以查看我的仓库。我在这里实现了github.com/nazmulweb/simple-mern-blog 我已经检查了你的仓库,但对我来说 client/src/api/index.js 似乎和我的一样,你能用代码解释一下吗。 如果和我的一样应该可以。你能分享你的回购吗?我可以在哪里检查你的整个代码? 【参考方案1】:

在您的 client/src/api/index.js 中,您没有返回 API 响应。您需要像这样返回响应

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

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

【讨论】:

以上是关于无法在 react-redux 中异步发布的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 thunk 在 react-redux 挂钩中进行异步调用?

react-redux 项目。 Chrome 开发工具不显示所有项目文件

Reactql - 使用 graphql 的 react-redux

无法从异步提交验证中的道具获取错误值

使用异步操作时,React-Redux组件测试失败

如何解决 react-redux 中的此错误“错误:操作必须是普通对象。使用自定义中间件进行异步操作。”