why is this happening? : TypeError: Cannot read property 'username' of undefined in REACT

Viewed 227

I'm trying to make a messenger clone app in react and I don't really know what happened to get this error:

TypeError: Cannot read property 'username' of undefined

why is it suddenly giving this error ??

please have a look!

so i have two files the first is App.js:

import React, { useState, useEffect } from "react";
import { Button, InputLabel, Input, FormControl } from "@material-ui/core";
import "./App.css";
import Message from "./Message";
import db from "./firebase";
import firebase from "firebase";
import FlipMove from "react-flip-move";

function App() {
  const [input, setInput] = useState("");
  const [messages, setMessages] = useState([{}]);
  const [username, setUsername] = useState("");

  useEffect(() => {
    db.collection("messages")
      .orderBy("timestamp", "desc")
      .onSnapshot((snapshot) => {
        setMessages(
          snapshot.docs.map((doc) => ({ id: doc.data, message: doc.data() }))
        );
      });
  }, []);

  useEffect(() => {
    setUsername(prompt("please enter your name"));
  }, []);

  const sendMessage = (event) => {
    event.preventDefault();
    db.collection("messages").add({
      message: input,
      username: username,
      timestamp: firebase.firestore.FieldValue.serverTimestamp(),
    });
    setInput("");
  };

  return (
    <div className="App">
      <h1>Hello</h1>
      <h2>welcome {username}</h2>
      <form className="send">
        <FormControl>
          <InputLabel>Enter a message..</InputLabel>
          <Input
            value={input}
            onChange={(event) => setInput(event.target.value)}
          />
          <Button
            disabled={!input}
            variant="contained"
            color="primary"
            type="submit"
            onClick={sendMessage}
          >
            Send Message
          </Button>
        </FormControl>
      </form>

      <FlipMove>
        {messages.map(({ id, message }) => (
          <Message key={id} username={username} message={message} />
        ))}
      </FlipMove>
    </div>
  );
}

export default App;

and the Second one is Message.js which the error occurs in:

import { Card, CardContent, Typography } from "@material-ui/core";
import React, { forwardRef } from "react";
import "./Message.css";

const Message = forwardRef(({ message, username }, ref) => {
  const isUser = username === message.username;
  return (
    <div ref={ref} className={`message ${isUser && "message_user"}`}>
      <Card className={isUser ? "message_userCard" : "message_guestCard"}>
        <CardContent>
          <Typography color="white" variant="h5" component="h2">
            {message.username}: {message.message}
          </Typography>
        </CardContent>
      </Card>
    </div>
  );
});

export default Message;

the error is in this line const isUser = username === message.username;

1 Answers

It means the message value that gets passed to the Message component as a prop is undefined.

So when you iterate through messages here, at least one of the objects in your messages array does not have a defined value for its message key:

      <FlipMove>
        {messages.map(({ id, message }) => (
          <Message key={id} username={username} message={message} />
        ))}
      </FlipMove>

Try printing out your entire messages array before the render() func and checking what values exist.

Try changing this line:

const [messages, setMessages] = useState([{}]);

To this:

const [messages, setMessages] = useState([]);

EDIT: The problem was that this following line means you are initializing your messages array to this: [{}]:

const [messages, setMessages] = useState([{}]);

This means your array has one element in it, an empty object {}. It will stay like this until your database read completes and populates your array with your real data. So the first time around, your render function will try to make sense of this array that only has this one empty object in it.

So when you iterate through it like this:

        {messages.map(({ id, message }) => (
          <Message key={id} username={username} message={message} />
        ))}

Then for every element, it will look for an id and message property because of this destructuring assignment (look that term up if you're not sure what that is):

({ id, message })

So because you start out with that one empty object element in your array, it will look for a .message property for that empty object, which won't exist. It will be undefined. This undefined gets passed as a prop to your Message component. Then in your Message component, this line:

const isUser = username === message.username;

Tries to access a .username property on undefined, because remember, message = undefined, because that's what you passed inadvertently as the prop. Thats why the error complains that it can't find a username property on something that is undefined:

TypeError: Cannot read property 'username' of undefined
Related