Search code examples
reactjsreact-hooksuse-statereact-statereact-state-management

Array of objects in state not updating correctly in React app


I am trying to make a list of messages and update it as I add more, I think I'm missing something but can't tell what it is.

App has 2 components - Messages and SendMessage, Messages is mapping over an array of Message Component. SenMessage has a form that will update the array store in messages state.

Thanks for any help in advance.

App.js

function App() {
const [messages, setMessages] = useState([{name: 'ken', 'message': "hayyy"},{name: 'kendra', 'message': "hayyy"}])

const handleSend = (message) => {
    const newMessages = [...messages];
    newMessages.push(message);
    setMessages(newMessages);
    console.log(message)
  }

return (
<div className="App">
<Messages messages={messages} />
<SendMessage onSend={handleSend}/>
</div>
  );
}
export default App;

Messages.js

const Messages = (props) => {
const messages = props.messages.map((message, index) => {
    return (
        <Message 
            key={index}
            name={message.name}
            message={message.message}
        />
    )})
return (
    <div>
    {messages}
    </div>
)}
export default Messages;

Message.js

const Message = (props) => {
    return (
        <div className="Message">
          <p className="message">{props.message}</p>
          <p className="name">{props.name}</p>
        </div>
    )}

export default Message;

SendMessage.js

const SendMessage = (props) => {
    const [inputMessage, setInputMessage] = useState('')
    const handleSubmit = (event) => {
        event.preventDefault();
        props.onSend(inputMessage);
        setInputMessage('')
    }
    const handleChange = (event) => {
        setInputMessage(event.target.value)
    }

    return (
        <div className="SendMessage">
          <form onSubmit={handleSubmit}>
            <input 
                type="text" 
                placeholder="enter message" 
                value={inputMessage}
                onChange={handleChange}
            />
            <button>Send</button>
          </form>
        </div>
    )}
export default SendMessage;

Solution

  • The main issue that I see here is that in the SendMessage component, you are saving the state to a string and trying to send to a handleSend function which expects an object with keys like name and message.

    Thus you can update your handleSend function like this:

    const handleSend = (message) => {
        const formattedMsg = { name: 'Some Name', message };
        const newMessages = [...messages, formattedMsg];
        setMessages(newMessages);
    }
    

    Also as a general practice use optional chaining whenever you are trying to use nested keys from objects. If you were using TypeScript, then this problem wouldn't occur in the first place since most probably you'd annotate the args with specific types. But here what we can do is atleast provide an optional chain(using ? operator) and handle the case where no messages exists.

    const messages = props.messages.map((message, index) => (
            <Message 
                key={index}
                name={message?.name}
                message={message?.message}
            />
    ));
    
    if (!messages) {
     return (
       <p>No messages...</p>
     )
    }
    

    [Edit] I now see that @DrewReese has commented a similar answer. Didn't see it before posting.