I am currently following a tutorial on youtube building a whatsapp clone with react and socket.io from webDevSimplified: https://www.youtube.com/watch?v=tBr-PybP_9c
and here the main repo : 'https://github.com/WebDevSimplified/Whatsapp-Clone/tree/master/client
I got stuck halfway through as for some reason my custom useConversation
hook returns undefined while my useContacts
hook works without any problems.
Here the setup:
App.js :
import Dashboard from "./Dashboard";
import { ContactsProvider } from "../contexts/ContactsProvider";
import { ConversationsProvider } from "../contexts/ConversationsProvider";
import Test from "../components/test";///test
console.log(Test)//test purpose
function App() {
const [id, setId] = useLocalStorage("id");
const dashboard = (
<ContactsProvider>
<ConversationsProvider id={id}>
<Dashboard id={id} />
</ConversationsProvider>
</ContactsProvider>
);
return id ? dashboard : <Login onIdSubmit={setId} />;
}
export default App;
ContactsProvider.js
import React, { useContext } from "react";
import useLocalStorage from "../hooks/useLocalStorage";
const ContactsContext = React.createContext();
export function useContacts() {
return useContext(ContactsContext);
}
export function ContactsProvider({ children }) {
const [contacts, setContacts] = useLocalStorage("contacts", []);//initalValue an empty array
function createContact(id, name) {
setContacts((prevContacts) => {
return [...prevContacts, { id, name }];
});
}
return (
<ContactsContext.Provider value={{ contacts, createContact }}>
{children}
</ContactsContext.Provider>
);
}
Contacts.js - here my useContacts
works
import React from "react";
import { ListGroup } from "react-bootstrap";
import { useContacts } from "../contexts/ContactsProvider";
export default function Contacts() {
const { contacts } = useContacts();
console.log(`contacts: ${contacts}`); //returns object, object as expected
return (
<ListGroup variant="flush">
{contacts.map((contact) => (//visibly working in UI when commenting out the ListGroup in conversations.js
<ListGroup.Item key={contact.id}>{contact.name}</ListGroup.Item>
))}
</ListGroup>
);
}
Here the problematic part:
ConversationsProvider.js
import React, { useContext } from "react";
import useLocalStorage from "../hooks/useLocalStorage";
import { useContacts } from "./ContactsProvider";
const ConversationsContext = React.createContext();
export function useConversations() {
return useContext(ConversationsContext);
}
export function ConversationsProvider({ children }) {
const [conversations, setConversations] = useLocalStorage(
"conversations", []);//as well empty array
const { contacts } = useContacts();
function createConversation(recipients) {
setConversations((prevConversations) => {
return [...prevConversations, { recipients, messages: [] }];
});
}
const formattedConversations = conversations.map((conversation) => {
const recipients = conversation.recipients.map((recipient) => {
const contact = contacts.find((contact) => {
return contact.id === recipient;
});
const name = (contact && contact.name) || recipient;
return { id: recipient, name };
});
return { ...conversation, recipients };
});
const value = {
conversations: formattedConversations,
createConversation,
};
return (
<ConversationsContext.Provider value={{ value }}>
{children}
</ConversationsContext.Provider>
);
}
and the component that causes the error:
Conversations.js:
import React from "react";
import { ListGroup } from "react-bootstrap";
import { useConversations } from "../contexts/ConversationsProvider";
export default function Conversations() {
const { conversations } = useConversations();
console.log( `conversations: ${conversations}`)//returns undefined
return (
<ListGroup variant="flush">
{conversations.map((conversation, index) => (//can't map because conversations is undefined
<ListGroup.Item key={index}>
{conversation.recipients
.map(r => r.name)
.join(", ")}
</ListGroup.Item>
))}
</ListGroup>
);
}
Here the localStorage setup for clarity:
import { useEffect, useState } from 'react'
const PREFIX = 'whatsapp-clone-'
export default function useLocalStorage(key, initialValue) {
const prefixedKey = PREFIX + key;
const [value, setValue] = useState(() => {
const jsonValue = localStorage.getItem(prefixedKey);
if (jsonValue != null) return JSON.parse(jsonValue);
if (typeof initialValue === "function") {
return initialValue();
} else {
return initialValue;
}
});
useEffect(() => {
localStorage.setItem(prefixedKey, JSON.stringify(value));
}, [prefixedKey, value]);
return [value, setValue];
}
I have been trying to solve this problem for hours and running out of ideas. I setup a test.js and imported the hooks in a Test.js function. Both return their objects respectively.
I have created the application using npx create-react-app and running it via yarn.
The problem lies within your
ConversationsProvider.js
:The following lines are all the same:
When you execute:
conversations
will be set toundefined
because the object returned will only have the propertyvalue
.To fix the issue remove the nesting by changing the line:
PS. Here a tip to improve your debugging skills. You already did:
Which logged
undefined
. The next step would be to not destruct the object immediately and log the whole context value instead.This would have shown that the object returned by
useConversations
is not what you expect it to be.