使用减速器函数时无法读取未定义的属性“conversationId”

Pra*_*ina 5 javascript reactjs redux react-redux

只是为了明确路由器使用下面的代码,我的messages.js在api文件夹中...... router.use("/messages", require("./messages")); 所以我的api调用是正确的。

用于发布消息的后端......我知道如果没有对话存在,conferenceId 将为空,但是......我正在尝试在对话已经存在的情况下发送消息,但我仍然无法读取未定义的对话 ID......

// expects {recipientId, text, conversationId } in body 
// (conversationId will be null if no conversation exists yet)
router.post("/", async (req, res, next) => {
  try {
    if (!req.user) {
      return res.sendStatus(401);
    }
    const senderId = req.user.id;
    const { recipientId, text, conversationId, sender } = req.body;

    // if we already know conversation id, we can save time and just add it to message and return
    if (conversationId) {
      const message = await Message.create({ senderId, text, conversationId });
      return res.json({ message, sender });
    }
    // if we don't have conversation id, find a conversation to make sure it doesn't already exist
    let conversation = await Conversation.findConversation(
      senderId,
      recipientId
    );

    if (!conversation) {
      // create conversation
      conversation = await Conversation.create({
        user1Id: senderId,
        user2Id: recipientId,
      });
      if (onlineUsers.includes(sender.id)) {
        sender.online = true;
      }
    }
    const message = await Message.create({
      senderId,
      text,
      conversationId: conversation.id,
    });
    res.json({ message, sender });
  } catch (error) {
    next(error);
  }
});

module.exports = router;
Run Code Online (Sandbox Code Playgroud)

这是将数据发布到后端的前端....

const saveMessage = async (body) => {
  const { data } = await axios.post("/api/messages", body);
  return data;
};
Run Code Online (Sandbox Code Playgroud)

好的,这里是有关我如何调度它的详细信息。

class Input extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: "",
    };
  }

  handleChange = (event) => {
    this.setState({
      text: event.target.value,
    });
  };

  handleSubmit = async (event) => {
    event.preventDefault();
    // add sender user info if posting to a brand new convo, 
    // so that the other user will have access to username, profile pic, etc.
    const reqBody = {
      text: event.target.text.value,
      recipientId: this.props.otherUser.id,
      conversationId: this.props.conversationId,
      sender: this.props.conversationId ? null : this.props.user,
    };
    await this.props.postMessage(reqBody);
    this.setState({
      text: "",
    });
  };

  render() {
    const { classes } = this.props;
    return (
      <form className={classes.root} onSubmit={this.handleSubmit}>
        <FormControl fullWidth hiddenLabel>
          <FilledInput
            classes={{ root: classes.input }}
            disableUnderline
            placeholder="Type something..."
            value={this.state.text}
            name="text"
            onChange={this.handleChange}
          />
        </FormControl>
      </form>
    );
  }
}


export default connect(
  mapStateToProps,
  mapDispatchToProps
)(withStyles(styles)(Input));



const mapDispatchToProps = (dispatch) => {
  return {
    postMessage: (message) => {
      dispatch(postMessage(message));
    },
  };
};



// message format to send: {recipientId, text, conversationId}
// conversationId will be set to null if its a brand new conversation
export const postMessage = (body) => (dispatch) => {
  try {
    const data = saveMessage(body);

    if (!body.conversationId) {
      dispatch(addConversation(body.recipientId, data.message));
    } else {
      dispatch(setNewMessage(data.message));
    }

    sendMessage(data, body);
  } catch (error) {
    console.error(error);
  }
};
Run Code Online (Sandbox Code Playgroud)

所以我已经附上了我现在想做的事情......但我仍然遇到问题......

// CONVERSATIONS THUNK CREATORS, this is how I am getting data from the backend

export const fetchConversations = () => async (dispatch) => {
  try {
    const { data } = await axios.get("/api/conversations");
    dispatch(gotConversations(data));
  } catch (error) {
    console.error(error);
  }
};


export const setNewMessage = (message, sender) => {
  return {
    type: SET_MESSAGE,
    payload: { message, sender: sender || null },
  };
};

// REDUCER

const reducer = (state = [], action) => {
  switch (action.type) {
    case GET_CONVERSATIONS:
      return action.conversations;
    case SET_MESSAGE:
      return addMessageToStore(state, action.payload);
    case ADD_CONVERSATION:
      return addNewConvoToStore(
        state,
        action.payload.recipientId,
        action.payload.newMessage
      );
    default:
      return state;
  }
};
Run Code Online (Sandbox Code Playgroud)

我收到一条错误消息,说在使用 reducer 函数时无法读取未定义的属性 'conversationId'... 我应该将消息的 setintial 值设置为空吗?

export const addMessageToStore = (state, payload) => {
  const { message, sender } = payload;
  // if sender isn't null, that means the message needs to be put in a brand new convo
  if (sender !== null) {
    const newConvo = {
      id: message.conversationId,
      otherUser: sender,
      messages: [message],
    };
    newConvo.latestMessageText = message.text;
    return [newConvo, ...state];
  }
  return state.map((convo) => {
    if (convo.id === message.conversationId) {
      const convoCopy = { ...convo };
      convoCopy.messages.push(message);
      convoCopy.latestMessageText = message.text;

      return convoCopy;
    } else {
      return convo;
    }
  });
};
Run Code Online (Sandbox Code Playgroud)

Dre*_*ese 5

问题

函数saveMessage已声明async

const saveMessage = async (body) => {
  const { data } = await axios.post("/api/messages", body);
  return data;
};
Run Code Online (Sandbox Code Playgroud)

postMessage操作创建者不是async,因此它不会等待隐式返回的 Promise 解决后再继续并分派到商店。这意味着这data.message是未定义的,因为Promise对象没有 this 作为属性。

export const postMessage = (body) => (dispatch) => {
  try {
    const data = saveMessage(body); // <-- no waiting

    if (!body.conversationId) {
      dispatch(addConversation(body.recipientId, data.message));
    } else {
      dispatch(setNewMessage(data.message));
    }

    sendMessage(data, body);
  } catch (error) {
    console.error(error);
  }
}; 
Run Code Online (Sandbox Code Playgroud)

解决方案

postMessage async并声明await响应data值。

export const postMessage = (body) => async (dispatch) => {
  try {
    const data = await saveMessage(body); // <-- await response

    if (!body.conversationId) {
      dispatch(addConversation(body.recipientId, data.message));
    } else {
      dispatch(setNewMessage(data.message));
    }

    sendMessage(data, body);
  } catch (error) {
    console.error(error);
  }
}; 
Run Code Online (Sandbox Code Playgroud)