Hello, I bought the course to create an application. I used this version: https://github.com/basir/mern-amazona.
I would like to integrate the chat part like here: https://github.com/basir/amazona/pull/81/files#diff-8bef997ba31a709dc2ea03358dbaba346057721bf6ff8b87d7a8cc06d457407.
I tried to add how they were here but I got errors:
THIS IS CHATBOX.JS:
import React, { useEffect, useRef, useState } from ‘react’; import socketIOClient from ‘socket.io-client’; const ENDPOINT = window.location.host.indexOf(‘localhost’) >= 0 ? ‘http://127.0.0.1:5000’ : window.location.host; export default function ChatBox(props) { const { userInfo } = props; const [socket, setSocket] = useState(null); const uiMessagesRef = useRef(null); const [isOpen, setIsOpen] = useState(false); const [messageBody, setMessageBody] = useState(”); const [messages, setMessages] = useState([ { name: ‘Admin’, body: ‘Hello there, Please ask your question.’ }, ]); useEffect(() => { if (uiMessagesRef.current) { uiMessagesRef.current.scrollBy({ top: uiMessagesRef.current.clientHeight, left: 0, behavior: ‘smooth’, }); } if (socket) { socket.emit(‘onLogin’, { _id: userInfo._id, name: userInfo.name, isAdmin: userInfo.isAdmin, }); socket.on(‘message’, (data) => { setMessages([…messages, { body: data.body, name: data.name }]); }); } }, [messages, isOpen, socket]); const supportHandler = () => { setIsOpen(true); console.log(ENDPOINT); const sk = socketIOClient(ENDPOINT); setSocket(sk); }; const submitHandler = (e) => { e.preventDefault(); if (!messageBody.trim()) { alert(‘Error. Please type message.’); } else { setMessages([…messages, { body: messageBody, name: userInfo.name }]); setMessageBody(”); setTimeout(() => { socket.emit(‘onMessage’, { body: messageBody, name: userInfo.name, isAdmin: userInfo.isAdmin, _id: userInfo._id, }); }, 1000); } }; const closeHandler = () => { setIsOpen(false); }; return ( <div className=”chatbox”> {!isOpen ? ( <button type=”button” onClick={supportHandler}> <i className=”fa fa-support” /> </button> ) : ( <div className=”card card-body”> <div className=”row”> <strong>Support </strong> <button type=”button” onClick={closeHandler}> <i className=”fa fa-close” /> </button> </div> <ul ref={uiMessagesRef}> {messages.map((msg, index) => ( <li key={index}> <strong>{`${msg.name}: `}</strong> {msg.body} </li> ))} </ul> <div> <form onSubmit={submitHandler} className=”row”> <input value={messageBody} onChange={(e) => setMessageBody(e.target.value)} type=”text” placeholder=”type message” /> <button type=”submit”>Send</button> </form> </div> </div> )} </div> ); }
THIS IS SUPPORTSCREEN.JS:
import React, { useEffect, useRef, useState } from ‘react’; import socketIOClient from ‘socket.io-client’; import { useSelector } from ‘react-redux’; import MessageBox from ‘../components/MessageBox’; let allUsers = []; let allMessages = []; let allSelectedUser = {}; const ENDPOINT = window.location.host.indexOf(‘localhost’) >= 0 ? ‘http://127.0.0.1:5000’ : window.location.host; export default function SupportScreen() { const [selectedUser, setSelectedUser] = useState({}); const [socket, setSocket] = useState(null); const uiMessagesRef = useRef(null); const [messageBody, setMessageBody] = useState(”); const [messages, setMessages] = useState([]); const [users, setUsers] = useState([]); const userSignin = useSelector((state) => state.userSignin); const { userInfo } = userSignin; useEffect(() => { if (uiMessagesRef.current) { uiMessagesRef.current.scrollBy({ top: uiMessagesRef.current.clientHeight, left: 0, behavior: ‘smooth’, }); } if (!socket) { const sk = socketIOClient(ENDPOINT); setSocket(sk); sk.emit(‘onLogin’, { _id: userInfo._id, name: userInfo.name, isAdmin: userInfo.isAdmin, }); sk.on(‘message’, (data) => { if (allSelectedUser._id === data._id) { allMessages = […allMessages, data]; } else { const existUser = allUsers.find((user) => user._id === data._id); if (existUser) { allUsers = allUsers.map((user) => user._id === existUser._id ? { …user, unread: true } : user ); setUsers(allUsers); } } setMessages(allMessages); }); sk.on(‘updateUser’, (updatedUser) => { const existUser = allUsers.find((user) => user._id === updatedUser._id); if (existUser) { allUsers = allUsers.map((user) => user._id === existUser._id ? updatedUser : user ); setUsers(allUsers); } else { allUsers = […allUsers, updatedUser]; setUsers(allUsers); } }); sk.on(‘listUsers’, (updatedUsers) => { allUsers = updatedUsers; setUsers(allUsers); }); sk.on(‘selectUser’, (user) => { allMessages = user.messages; setMessages(allMessages); }); } }, [messages, socket, users]); const selectUser = (user) => { allSelectedUser = user; setSelectedUser(allSelectedUser); const existUser = allUsers.find((x) => x._id === user._id); if (existUser) { allUsers = allUsers.map((x) => x._id === existUser._id ? { …x, unread: false } : x ); setUsers(allUsers); } socket.emit(‘onUserSelected’, user); }; const submitHandler = (e) => { e.preventDefault(); if (!messageBody.trim()) { alert(‘Error. Please type message.’); } else { allMessages = [ …allMessages, { body: messageBody, name: userInfo.name }, ]; setMessages(allMessages); setMessageBody(”); setTimeout(() => { socket.emit(‘onMessage’, { body: messageBody, name: userInfo.name, isAdmin: userInfo.isAdmin, _id: selectedUser._id, }); }, 1000); } }; return ( <div className=”row top full-container”> <div className=”col-1 support-users”> {users.filter((x) => x._id !== userInfo._id).length === 0 && ( <MessageBox>No Online User Found</MessageBox> )} <ul> {users .filter((x) => x._id !== userInfo._id) .map((user) => ( <li key={user._id} className={user._id === selectedUser._id ? ‘ selected’ : ‘ ‘} > <button className=”block” type=”button” onClick={() => selectUser(user)} > {user.name} </button> <span className={ user.unread ? ‘unread’ : user.online ? ‘online’ : ‘offline’ } /> </li> ))} </ul> </div> <div className=”col-3 support-messages”> {!selectedUser._id ? ( <MessageBox>Select a user to start chat</MessageBox> ) : ( <div> <div className=”row”> <strong>Chat with {selectedUser.name} </strong> </div> <ul ref={uiMessagesRef}> {messages.length === 0 && <li>No message.</li>} {messages.map((msg, index) => ( <li key={index}> <strong>{`${msg.name}: `}</strong> {msg.body} </li> ))} </ul> <div> <form onSubmit={submitHandler} className=”row”> <input value={messageBody} onChange={(e) => setMessageBody(e.target.value)} type=”text” placeholder=”Type message here…” required ></input> <button type=”submit”>Send</button> </form> </div> </div> )} </div> </div> ); }
THIS IS SERVER.JS:
import http from ‘http’; import { Server } from ‘socket.io’; import express from ‘express’; import path from ‘path’; import mongoose from ‘mongoose’; import dotenv from ‘dotenv’; import seedRouter from ‘./routes/seedRoutes.js’; import productRouter from ‘./routes/productRoutes.js’; import userRouter from ‘./routes/userRoutes.js’; import orderRouter from ‘./routes/orderRoutes.js’; import uploadRouter from ‘./routes/uploadRoutes.js’; dotenv.config(); mongoose.set(‘strictQuery’, false); mongoose .connect(process.env.MONGODB_URI) .then(() => { console.log(‘connected to db’); }) .catch((err) => { console.log(err.message); }); const app = express(); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.get(‘/api/keys/paypal’, (req, res) => { res.send(process.env.PAYPAL_CLIENT_ID || ‘sb’); }); app.get(‘/api/keys/google’, (req, res) => { res.send({ key: process.env.GOOGLE_API_KEY || ” }); }); app.use(‘/api/upload’, uploadRouter); app.use(‘/api/seed’, seedRouter); app.use(‘/api/products’, productRouter); app.use(‘/api/users’, userRouter); app.use(‘/api/orders’, orderRouter); const __dirname = path.resolve(); app.use(express.static(path.join(__dirname, ‘/frontend/build’))); app.get(‘*’, (req, res) => res.sendFile(path.join(__dirname, ‘/frontend/build/index.html’)) ); app.use((err, req, res, next) => { res.status(500).send({ message: err.message }); }); const port = process.env.PORT || 5000; const httpServer = http.Server(app); const io = new Server(httpServer, { cors: { origin: ‘*’ } }); const users = []; io.on(‘connection’, (socket) => { console.log(‘connection’, socket.id); socket.on(‘disconnect’, () => { const user = users.find((x) => x.socketId === socket.id); if (user) { user.online = false; console.log(‘Offline’, user.name); const admin = users.find((x) => x.isAdmin && x.online); if (admin) { io.to(admin.socketId).emit(‘updateUser’, user); } } }); socket.on(‘onLogin’, (user) => { const updatedUser = { …user, online: true, socketId: socket.id, messages: [], }; const existUser = users.find((x) => x._id === updatedUser._id); if (existUser) { existUser.socketId = socket.id; existUser.online = true; } else { users.push(updatedUser); } console.log(‘Online’, user.name); const admin = users.find((x) => x.isAdmin && x.online); if (admin) { io.to(admin.socketId).emit(‘updateUser’, updatedUser); } if (updatedUser.isAdmin) { io.to(updatedUser.socketId).emit(‘listUsers’, users); } }); socket.on(‘onUserSelected’, (user) => { const admin = users.find((x) => x.isAdmin && x.online); if (admin) { const existUser = users.find((x) => x._id === user._id); io.to(admin.socketId).emit(‘selectUser’, existUser); } }); socket.on(‘onMessage’, (message) => { if (message.isAdmin) { const user = users.find((x) => x._id === message._id && x.online); if (user) { io.to(user.socketId).emit(‘message’, message); user.messages.push(message); } } else { const admin = users.find((x) => x.isAdmin && x.online); if (admin) { io.to(admin.socketId).emit(‘message’, message); const user = users.find((x) => x._id === message._id && x.online); user.messages.push(message); } else { io.to(socket.id).emit(‘message’, { name: ‘Admin’, body: ‘Sorry. I am not online right now’, }); } } }); }); httpServer.listen(port, () => { console.log(`serve at http://localhost:${port}`); });
AND THIS IS APP.JS:
import { BrowserRouter, Link, Route, Routes } from ‘react-router-dom’; import { toast, ToastContainer } from ‘react-toastify’; import ‘react-toastify/dist/ReactToastify.css’; import HomeScreen from ‘./screens/HomeScreen’; import ProductScreen from ‘./screens/ProductScreen’; import Navbar from ‘react-bootstrap/Navbar’; import Badge from ‘react-bootstrap/Badge’; import Nav from ‘react-bootstrap/Nav’; import NavDropdown from ‘react-bootstrap/NavDropdown’; import Container from ‘react-bootstrap/Container’; import { LinkContainer } from ‘react-router-bootstrap’; import { useContext, useEffect, useState } from ‘react’; import { Store } from ‘./Store’; import CartScreen from ‘./screens/CartScreen’; import SigninScreen from ‘./screens/SigninScreen’; import ShippingAddressScreen from ‘./screens/ShippingAddressScreen’; import SignupScreen from ‘./screens/SignupScreen’; import PaymentMethodScreen from ‘./screens/PaymentMethodScreen’; import PlaceOrderScreen from ‘./screens/PlaceOrderScreen’; import OrderScreen from ‘./screens/OrderScreen’; import OrderHistoryScreen from ‘./screens/OrderHistoryScreen’; import ProfileScreen from ‘./screens/ProfileScreen’; import Button from ‘react-bootstrap/Button’; import { getError } from ‘./utils’; import axios from ‘axios’; import SearchBox from ‘./components/SearchBox’; import SearchScreen from ‘./screens/SearchScreen’; import ProtectedRoute from ‘./components/ProtectedRoute’; import DashboardScreen from ‘./screens/DashboardScreen’; import AdminRoute from ‘./components/AdminRoute’; import ProductListScreen from ‘./screens/ProductListScreen’; import ProductEditScreen from ‘./screens/ProductEditScreen’; import OrderListScreen from ‘./screens/OrderListScreen’; import UserListScreen from ‘./screens/UserListScreen’; import UserEditScreen from ‘./screens/UserEditScreen’; import MapScreen from ‘./screens/MapScreen’; import SupportScreen from ‘./screens/SupportScreen’; import ChatBox from ‘./components/ChatBox’; function App() { const { state, dispatch: ctxDispatch } = useContext(Store); const { fullBox, cart, userInfo } = state; const signoutHandler = () => { ctxDispatch({ type: ‘USER_SIGNOUT’ }); localStorage.removeItem(‘userInfo’); localStorage.removeItem(‘shippingAddress’); localStorage.removeItem(‘paymentMethod’); window.location.href = ‘/signin’; }; const [sidebarIsOpen, setSidebarIsOpen] = useState(false); const [categories, setCategories] = useState([]); useEffect(() => { const fetchCategories = async () => { try { const { data } = await axios.get(`/api/products/categories`); setCategories(data); } catch (err) { toast.error(getError(err)); } }; fetchCategories(); }, []); return ( <BrowserRouter> <div className={ sidebarIsOpen ? fullBox ? ‘site-container active-cont d-flex flex-column full-box’ : ‘site-container active-cont d-flex flex-column’ : fullBox ? ‘site-container d-flex flex-column full-box’ : ‘site-container d-flex flex-column’ } > <ToastContainer position=”bottom-center” limit={1} /> <header> <Navbar bg=”dark” variant=”dark” expand=”lg”> <Container> <Button variant=”dark” onClick={() => setSidebarIsOpen(!sidebarIsOpen)} > <i className=”fas fa-bars”></i> </Button> <LinkContainer to=”/”> <Navbar.Brand>amazona</Navbar.Brand> </LinkContainer> <Navbar.Toggle aria-controls=”basic-navbar-nav” /> <Navbar.Collapse id=”basic-navbar-nav”> <SearchBox /> <Nav className=”me-auto w-100 justify-content-end”> <Link to=”/cart” className=”nav-link”> Cart {cart.cartItems.length > 0 && ( <Badge pill bg=”danger”> {cart.cartItems.reduce((a, c) => a + c.quantity, 0)} </Badge> )} </Link> {userInfo ? ( <NavDropdown title={userInfo.name} id=”basic-nav-dropdown”> <LinkContainer to=”/profile”> <NavDropdown.Item>User Profile</NavDropdown.Item> </LinkContainer> <LinkContainer to=”/orderhistory”> <NavDropdown.Item>Order History</NavDropdown.Item> </LinkContainer> <NavDropdown.Divider /> <Link className=”dropdown-item” to=”#signout” onClick={signoutHandler} > Sign Out </Link> </NavDropdown> ) : ( <Link className=”nav-link” to=”/signin”> Sign In </Link> )} {userInfo && userInfo.isAdmin && ( <NavDropdown title=”Admin” id=”admin-nav-dropdown”> <LinkContainer to=”/admin/dashboard”> <NavDropdown.Item>Dashboard</NavDropdown.Item> </LinkContainer> <LinkContainer to=”/admin/products”> <NavDropdown.Item>Products</NavDropdown.Item> </LinkContainer> <LinkContainer to=”/admin/orders”> <NavDropdown.Item>Orders</NavDropdown.Item> </LinkContainer> <LinkContainer to=”/admin/users”> <NavDropdown.Item>Users</NavDropdown.Item> </LinkContainer> <LinkContainer to=”/admin/support”> <NavDropdown.Item>Support</NavDropdown.Item> </LinkContainer> </NavDropdown> )} </Nav> </Navbar.Collapse> </Container> </Navbar> </header> <div className={ sidebarIsOpen ? ‘active-nav side-navbar d-flex justify-content-between flex-wrap flex-column’ : ‘side-navbar d-flex justify-content-between flex-wrap flex-column’ } > <Nav className=”flex-column text-white w-100 p-2″> <Nav.Item> <strong>Categories</strong> </Nav.Item> {categories.map((category) => ( <Nav.Item key={category}> <LinkContainer to={{ pathname: ‘/search’, search: `category=${category}` }} onClick={() => setSidebarIsOpen(false)} > <Nav.Link>{category}</Nav.Link> </LinkContainer> </Nav.Item> ))} </Nav> </div> <main> <Container className=”mt-3″> <Routes> <Route path=”/product/:slug” element={<ProductScreen />} /> <Route path=”/cart” element={<CartScreen />} /> <Route path=”/search” element={<SearchScreen />} /> <Route path=”/signin” element={<SigninScreen />} /> <Route path=”/signup” element={<SignupScreen />} /> <Route path=”/profile” element={ <ProtectedRoute> <ProfileScreen /> </ProtectedRoute> } /> <Route path=”/map” element={ <ProtectedRoute> <MapScreen /> </ProtectedRoute> } /> <Route path=”/placeorder” element={<PlaceOrderScreen />} /> <Route path=”/order/:id” element={ <ProtectedRoute> <OrderScreen /> </ProtectedRoute> } ></Route> <Route path=”/orderhistory” element={ <ProtectedRoute> <OrderHistoryScreen /> </ProtectedRoute> } ></Route> <Route path=”/shipping” element={<ShippingAddressScreen />} ></Route> <Route path=”/payment” element={<PaymentMethodScreen />}></Route> {/* Admin Routes */} <Route path=”/admin/dashboard” element={ <AdminRoute> <DashboardScreen /> </AdminRoute> } ></Route> <Route path=”/admin/orders” element={ <AdminRoute> <OrderListScreen /> </AdminRoute> } ></Route> <Route path=”/admin/users” element={ <AdminRoute> <UserListScreen /> </AdminRoute> } ></Route> <Route path=”/admin/support” element={ <AdminRoute> <SupportScreen /> </AdminRoute> } ></Route> <Route path=”/admin/products” element={ <AdminRoute> <ProductListScreen /> </AdminRoute> } ></Route> <Route path=”/admin/product/:id” element={ <AdminRoute> <ProductEditScreen /> </AdminRoute> } ></Route> <Route path=”/admin/user/:id” element={ <AdminRoute> <UserEditScreen /> </AdminRoute> } ></Route> <Route path=”/” element={<HomeScreen />} /> </Routes> </Container> </main> <footer> <div className=”text-center”> {userInfo && !userInfo.isAdmin && <ChatBox userInfo={userInfo} />} <div>All right reserved</div>{‘ ‘} </div> </footer> </div> </BrowserRouter> ); } export default App;
THIS IS MY ERROR:
useReduxContext.js:24 Uncaught Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>
at useReduxContext (useReduxContext.js:24:1)
at useSelector (useSelector.js:40:1)
at SupportScreen (SupportScreen.js:21:1)
at renderWithHooks (react-dom.development.js:16305:1)
at mountIndeterminateComponent (react-dom.development.js:20074:1)
at beginWork (react-dom.development.js:21587:1)
at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
at invokeGuardedCallback (react-dom.development.js:4277:1)
at beginWork$1 (react-dom.development.js:27451:1)
react-dom.development.js:18687 The above error occurred in the <SupportScreen> component:
at SupportScreen (http://localhost:3000/static/js/bundle.js:8981:90)
at AdminRoute (http://localhost:3000/static/js/bundle.js:998:5)
at RenderedRoute (http://localhost:3000/static/js/bundle.js:73955:5)
at Routes (http://localhost:3000/static/js/bundle.js:74506:5)
at div
at http://localhost:3000/static/js/bundle.js:39982:5
at main
at div
at Router (http://localhost:3000/static/js/bundle.js:74444:15)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:72697:5)
at App (http://localhost:3000/static/js/bundle.js:105:56)
at PayPalScriptProvider (http://localhost:3000/static/js/bundle.js:10892:15)
at r (http://localhost:3000/static/js/bundle.js:69363:21)
at StoreProvider (http://localhost:3000/static/js/bundle.js:918:78)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
logCapturedError @ react-dom.development.js:18687
react-dom.development.js:12056 Uncaught Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>
at useReduxContext (useReduxContext.js:24:1)
at useSelector (useSelector.js:40:1)
at SupportScreen (SupportScreen.js:21:1)
at renderWithHooks (react-dom.development.js:16305:1)
at mountIndeterminateComponent (react-dom.development.js:20074:1)
at beginWork (react-dom.development.js:21587:1)
at beginWork$1 (react-dom.development.js:27426:1)
at performUnitOfWork (react-dom.development.js:26557:1)
at workLoopSync (react-dom.development.js:26466:1)
at renderRootSync (react-dom.development.js:26434:1)
I need to integrate this chat in my application. Help me to solve the error. Please give me the correct code to be able to integrate this with my application and get rid of the error. Please help me as fast as possible!!