I am trying to make a WhatsApp Web clone using React and Firebase. Here I want to implement the functionality where clicking on a specific chat in the sidebar on left of window opens a chat body on right.
For this I am using Switch and Route in React. By default, I want to show the sidebar in all cases but display the Chat body only when the user clicks on one of the chats.
For this the code in my App.js is :
import React from "react";
import Chat from "./Chat";
import { BrowserRouter,Switch, Route } from "react-router-dom";
import "./App.css";
import Sidebar from "./Sidebar";
function App() {
return (
<div className="app">
<div className="app_body">
<BrowserRouter>
<Switch>
<Sidebar />
<Route exact path="/" component={Chat} />
<Route path="/rooms/:roomId" component={Chat} />
</Switch>
</BrowserRouter>
</div>
</div>
);
}
export default App;
However, only the sidebar gets displayed every time.The chat doesn't get displayed even when I have added the component Chat in my exact home route ("/")
as shown here
When I interchange the Chat and Sidebar components in the code, only the Chat component gets displayed like this
What is wrong in this code? I tried to search for a lot of answers but couldn't find any answers to this. I tried switching routes, components and everything
The codes for my specific components are :
Chat :
import React, { useState, useEffect } from "react";
import "./Chat.css";
import { Avatar, IconButton } from "#material-ui/core";
import {
Search,
AttachFile,
MoreVert,
InsertEmoticon,
Mic,
} from "#material-ui/icons";
import { useParams } from "react-router-dom";
import db from "./firebase";
function Chat() {
const [input, setInput] = useState("");
const { roomId } = useParams();
const [roomName, setRoomName] = useState("");
useEffect(() => {
if (roomId) {
db.collection("rooms")
.doc(roomId)
.onSnapshot((snapshot) => setRoomName(snapshot.data().name));
}
}, [roomId]);
const sendMessage = (e) => {
e.preventDefault();
console.log(input);
setInput("");
};
return (
<div className="chat">
<div className="chat_header">
<Avatar src="https://res.cloudinary.com/shatadrucld/image/upload/v1597305602/qpqnqwyuokbupddqwuwc.jpg" />
<div className="chat_headerInfo">
<h3>{roomName}</h3>
<p>Last seen at .....</p>
</div>
<div className="chat_headerRight">
<IconButton>
<Search />
</IconButton>
<IconButton>
<AttachFile />
</IconButton>
<IconButton>
<MoreVert />
</IconButton>
</div>
</div>
<div className="chat_body">
<p className={`chat_message ${true && "chat-receiver"}`}>
<span className="chat_name">Shatadru Roy</span>Hey guys
<span className="chat_timestamp">3:52pm</span>
</p>
</div>
<div className="chat_footer">
<InsertEmoticon />
<form>
<input
type="text"
placeholder="Type a message"
value={input}
onChange={(e) => {
setInput(e.target.value);
}}
></input>
<button type="submit" onClick={sendMessage}></button>
</form>
<Mic />
</div>
</div>
);
}
export default Chat;
Sidebar :
import SidebarChat from "./SidebarChat";
import db from "./firebase";
function Sidebar() {
const [rooms, setRooms] = useState([]);
useEffect(() => {
const unsubscribe=db.collection("rooms").onSnapshot((snapshot) => {
setRooms(
snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
);
});
return ()=>{
unsubscribe();
}
}, []);
return (
<div className="sidebar">
<div className="sidebar_header">
<Avatar />
<div className="sidebar_headerRight">
<IconButton>
<DonutLarge />
</IconButton>
<IconButton>
<Chat />
</IconButton>
<IconButton>
<MoreVert />
</IconButton>
</div>
</div>
<div className="sidebar_search">
<div className="sidebar_searchContainer">
<SearchOutlined />
<input placeholder="Seacrh chats" type="text"></input>
</div>
</div>
<div className="sidebar_chats">
<SidebarChat addNewChat />
{rooms.map(room=>{
return (<SidebarChat key={room.id} id={room.id} name={room.data.name} />)
})}
</div>
</div>
);
}
export default Sidebar;
Please help me with this problem. I am new to these concepts so I am completely stuck at this phase. I tried several solutions in the internet but none of them is working.
Edit :
I also tried to use withRouter but it is not working either
Try moving <Sidebar /> out of the <Switch> component. If is persistent then it should be outside of <Switch/>
Related
I'm learning reactjs and I'm trying to simulate an "Authentication" method on the front-end with reactjs and json-server and I'm facing a problem.
I have theses components:
Apps.js (with all the Routes)
Login.jsx ( with a form and all the logic )
ProtectedRoutes.jsx (as a function component to do a simple verification if the user is logged or no, and protected routes.
Clients.jsx (with all the lists fetched from the json-server, working properly, not important here)
I would like to create one state (isLogged / setIsLogged) to be trigged as "true" when the user hit the submit on my "Login.jsx", reusing this component, since the current state is "false". But I'm not figuring out how to do it. I'm not understanding how I can access the functions / state to do this.
App.js
import "./App.css";
import Login from "./pages/Login";
import Register from "./pages/Register";
import Clients from "./pages/Clients";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import ProtectedRoutes from "./components/ProtectedRoutes";
function App() {
return (
<>
<Router>
<Routes>
<Route path="/" element={<Login />} />
<Route element={<ProtectedRoutes isLogged={false} />}>
<Route path="/register" element={<Register />} />
<Route path="/clients" element={<Clients />} />
</Route>
</Routes>
</Router>
</>
);
}
export default App;
ProtectedRoutes.jsx
import { Navigate, Outlet } from "react-router-dom";
const ProtectedRoutes = ({ isLogged }) => {
return isLogged ? <Outlet /> : <Navigate to="/" />;
};
export default ProtectedRoutes;
Login.jsx
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { validateEmail, validatePassword } from "../utils/regex";
import logoImg from "../assets/logo-troupe.png";
import Navbar from "../components/Navbar";
const Login = () => {
const navigateTo = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [emailErr, setEmailErr] = useState(false);
const [passwordErr, setPasswordErr] = useState(false);
// input login validations
const validateEmailPassword = () => {
if (!validateEmail.test(email)) {
setEmailErr(true);
} else {
setEmailErr(false);
}
if (!validatePassword.test(password)) {
setPasswordErr(true);
} else {
setPasswordErr(false);
navigateTo("/clients");
}
};
//function to generate a random "token" simulating a login on backend
const handleStorageToken = () => {
const userToken = {
email,
password,
};
localStorage.setItem("Token", JSON.stringify(userToken));
};
const handleSubmit = (e) => {
e.preventDefault();
validateEmailPassword();
handleStorageToken();
};
return (
<div className="main-container">
<Navbar />
<div className="login-container">
<a href="#">
<img src={logoImg} alt="logo" tooltip="Troupe website" />
</a>
<h1>Login</h1>
<form>
<div className="form-group">
<label>E-mail</label>
<input
type="email"
placeholder="Enter your e-mail"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
{emailErr && (
<p className="validation-error">
Insira um e-mail válido!
</p>
)}
</div>
<div className="form-group">
<label>Password</label>
<input
type="password"
placeholder="Enter your password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{passwordErr && (
<p className="validation-error">
Senha inválida! Mínimo 4 caracteres, 1 letra and 1
número
</p>
)}
<button onClick={handleSubmit} className="btn-login" type="submit">
Login
</button>
</div>
</form>
</div>
</div>
);
};
export default Login;
I have a button that represents shop now that upon click it redirects you on different pages and the card elements have been mapped on external data.
I want this button to navigate to different pages kindly help.
const data =[
{
id:"Extensions",
Title:"Electrical Collections",
img:"/Assets/Electricals/Extlogo.png",
},
{
id:"Phone Accesorries",
Title:"Phone Accessories",
img:"/Assets/Phone/phoneacc.png",
},
{
id:"Homeware",
Title:"Homeware Collections",
img:"/Assets/Home/home.png",
},
]
function Home() {
let Navigate =useNavigate();
const handleElectricalPage = () =>{
Navigate("/extensionproduct")
}
<div className='cardContainer'>
{data.map((item )=>(
<card>
<div className='imageContainer'>
<img src={item.img} alt='Extension logo'/>
</div>
<div className='contentsContainer'>
<h2>{item.Title}</h2>
<div className='iconButtonContainer'>
<button onClick={handleElectricalPage}>Shop Now</button>
<ArrowForwardIcon className='arrowIcon'/>
</div>
</div>
Example from the react router https://reactrouter.com/docs/en/v6/api#usenavigate
navigate("../success", { replace: true });
You need to make an onClick handler (handleElectricalPage) dynamically, consider something like this.
function Home() {
let navigate =useNavigate();
return (
<div className='cardContainer'>
{data.map((item) => (
<card>
<button
onClick={() => navigate(`/externalProduct/${item.id}`)}
>
Shop Now
</button>
</card>
)}
</div>
}
You can also use Link which handles everything by itself
<Link to={`/externalProduct/${item.id}`}>
<button>Shop now</button>
</Link>
App.jsx
import { Routes, Route, Navigate } from "react-router-dom";
function App() {
return (
<div className="App">
<Routes>
<Route path="/" element={<Home />}>
<Route path="post/:id" element={<Detail />} />
</Route>
</Routes>
</div>
);
}
export default App;
Post.jsx
import React, { useState } from "react";
import {useNavigate} from 'react-router-dom'
export default function Post(props) {
const navigate = useNavigate()
{props.posts.map((post) => {
return (
<div className="post__container" key={post.id}>
<h4>
{post.id} {post.title}
</h4>
<p>{post.body}</p>
<button onClick={() => {return navigate(`${post.id}`)}}>Detail</button>
</div>
);
})}
}
Detail.jsx
import axios from 'axios'
import React, {useEffect, useState } from 'react'
import {useParams} from 'react-router'
export default function Detail (props) {
const params = useParams()
const [posts, setPosts] = useState({})
async function getById(id) {
const response = await axios.get("https://jsonplaceholder.typicode.com/posts/" + id)
setPosts(response.data)
}
useEffect(() => {
getById(params.id)
}, [params.id])
return (
<div>
<h2>Detail page: {params.id}</h2>
<h4>{posts.title}</h4>
<h5>{posts.body}</h5>
</div>
)
}
I have an Chat application.
In the App.js function component I have a messages state initialized as an empty array.
import React, { useState, useEffect } from 'react';
import './App.css';
import Sidebar from './Sidebar';
import Chat from './Chat';
import Pusher from 'pusher-js';
import axios from './axios';
function App() {
const [messages, setMessages] = useState([]);
useEffect(() => {
axios.get('http://localhost:9000/messages/sync')
.then(response => {
console.log(response.data);
setMessages(response.data);
})
}, [])
// when function component loads, run the follow once
useEffect(() => {
const pusher = new Pusher('1693ef51485e86ca1e9f', {
cluster: 'us2',
});
const channel = pusher.subscribe('messages');
channel.bind('inserted', (newMessage) => {
alert(JSON.stringify(newMessage));
setMessages([...messages, newMessage]);
});
return () => {
channel.unbind_all();
channel.unsubscribe();
};
}, [messages])
return (
<div className="app">
<div className="app_body">
<Sidebar />
<Chat messsages={messages}/>
</div>
</div>
);
}
export default App;
Once messages are stored there, the messages state is passed down as props to a function component in Chat.js.
import { Avatar, IconButton } from '#material-ui/core';
import SearchOutlinedIcon from '#material-ui/icons/SearchOutlined';
import AttachFileIcon from '#material-ui/icons/AttachFile';
import MoreVertIcon from "#material-ui/icons/MoreVert";
import InsertEmoticonIcon from '#material-ui/icons/InsertEmoticon';
import MicIcon from '#material-ui/icons/Mic';
import React from 'react';
import './Chat.css';
function Chat({ messages }) {
console.log(messages)
return (
<div className="chat">
<div className="chat_header">
<Avatar />
<div className="chat_headerInfo">
<h3>Room Name</h3>
<p>Last seen at...</p>
</div>
<div className="chat_headerRight">
<IconButton>
<SearchOutlinedIcon />
</IconButton>
<IconButton>
<AttachFileIcon />
</IconButton>
<IconButton>
<MoreVertIcon />
</IconButton>
</div>
</div>
<div className="chat_body">
{console.log(messages)}
{/**messages.map((message) => {
<p className={`chat_message ${message.received && "chat_receiver"}`}>
<span className="chat_name">{message.name}</span>
{message.message}
<span className="chat_timestamp">
{message.timestamp}
</span>
</p>
}) */}
</div>
<div className="chat_footer">
<InsertEmoticonIcon />
<form>
<input placeholder="Type a message"
type="text"
/>
<button type="submit">
Send a message
</button>
</form>
<MicIcon />
</div>
</div>
)
}
export default Chat
However messages is undefined in Chat.
Where is the error? I've experimented with passing props to chat as:
function Chat (props) {
{console.log(props.message)}
}
Nothing seems to work.
Check the spelling tripple sss - messsages in
return (
<div className="app">
<div className="app_body">
<Sidebar />
<Chat messsages={messages}/>
</div>
</div>
);
It's quite possible that your request to localhost in the first useEffect hook is not returning a value with the response. Double check your console output on that. As written, it should be working.
If that seems perfectly fine, then I would say that you need to clear your build cache.
I'm making a react application and whenever I search for something(eg cat) on the homepage, the url changes to search/cat and the forward, backward button work normally & help me switch between the homepage and the cat search page ...but when i search for something again (eg rat) after(homepage->cat) so the url changes to search/rat? and now when i press the back button the url changes to search/rat and i'm on the same page then if i press back button again the url becomes search/cat but the page still has the results of the rat search and if i press back again ,the homepage appears..why is this happening?I think it's because of the ? that appears at the end of the url..Please help
after searching cat
after searching for rat
after pressing the back button
after pressing the back button
after pressing the back button
This is the code of the search bar
import React, { Component } from "react";
import "./styles/searchBar.scss";
import "font-awesome/css/font-awesome.min.css";
import { withRouter } from "react-router-dom";
import SearchForm from "./SearchForm";
import { connect } from "react-redux";
import { fetchRandomPhotos } from "../redux/actions/randomPhotoAction";
class SearchBar extends Component {
state = {
searchQuery: "",
};
componentDidMount() {
this.props.fetchRandomPhotos();
}
handleChange = (event) => {
this.setState({ searchQuery: event.target.value });
};
handleSubmit = (event) => {
//event.preventDefault();
this.props.history.push(`/search/${this.state.searchQuery}`);
};
handleProfile = () => {
this.props.history.push(`/public/${this.props.photo.user.username}`);
};
render() {
const { photo } = this.props;
return !photo ? (
<div className="search-bar-container">
<div className="search-bar-area">
<div className="about-foto-fab">
<h1>Foto-Fab</h1>
<p>The internet's source of freely-usable images.</p>
<p>Powered by creator everywhere</p>
</div>
<SearchForm
onSubmit={this.handleSubmit}
onChange={this.handleChange}
/>
</div>
</div>
) : (
<div
className="search-bar-container"
style={{ backgroundImage: `url("${photo.urls.full}")` }}
>
<div className="black-layer"></div>
<div className="search-bar-area">
<div className="about-foto-fab">
<h1>Foto-Fab</h1>
<p>The internet's source of freely-usable images.</p>
<p>Powered by creator everywhere</p>
</div>
<SearchForm
onSubmit={this.handleSubmit}
onChange={this.handleChange}
/>
</div>
<div className="picture-info">
<div className="photographer">
<p onClick={this.handleProfile}>
<strong>Photo</strong> by {""}
<strong>{photo.user.name}</strong>
</p>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
photo: state.randomPhotoState.photo,
};
};
export default connect(mapStateToProps, { fetchRandomPhotos })(
withRouter(SearchBar)
);
This is the App.js
import React from "react";
import Navbar from "./components/Navbar";
import { BrowserRouter, Switch, Route, Redirect } from "react-router-dom";
import Home from "./pages/Home";
import LoginPage from "./pages/LoginPage";
import ProfilePage from "./pages/ProfilePage";
import SearchPage from "./pages/SearchPage";
import PublicUserProfilePage from "./pages/publicUserProfilePage";
import MobileNavigation from "./components/MobileNavigation";
import AboutPage from "./pages/AboutPage";
function App() {
return (
<BrowserRouter>
<Navbar />
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/login" component={LoginPage} />
<Route exact path="/profile" component={ProfilePage} />
<Route exact path="/search/:searchQuery" component={SearchPage} />
<Route exact path="/about" component={AboutPage} />
<Route
exact
path="/public/:username"
component={PublicUserProfilePage}
/>
<Redirect to="/" />
</Switch>
<MobileNavigation />
</BrowserRouter>
);
}
export default App;
search form component
import React, { Component } from "react";
export class SearchForm extends Component {
render() {
const { onSubmit, onChange } = this.props;
return (
<form className="search-form" onSubmit={onSubmit}>
<input
type="text"
placeholder="Search free high-resolution photos"
onChange={onChange}
/>
<button type="submit">
<i className="fa fa-search"></i>
</button>
</form>
);
}
}
export default SearchForm;
import React, { Component } from "react";
import "./styles/searchBar.scss";
import "font-awesome/css/font-awesome.min.css";
import { withRouter } from "react-router-dom";
import SearchForm from "./SearchForm";
import { connect } from "react-redux";
import { fetchRandomPhotos } from "../redux/actions/randomPhotoAction";
class SearchBar extends Component {
state = {
searchQuery: "",
};
componentDidMount() {
this.props.fetchRandomPhotos();
}
handleChange = (event) => {
this.setState({ searchQuery: event.target.value });
};
handleSubmit = (event) => {
event.preventDefault();
if (this.state.searchQuery) {
this.props.history.push(`/search/${this.state.searchQuery}`);
}
};
handleProfile = () => {
this.props.history.push(`/public/${this.props.photo.user.username}`);
};
render() {
const { photo } = this.props;
return !photo ? (
<div className="search-bar-container">
<div className="search-bar-area">
<div className="about-foto-fab">
<h1>Foto-Fab</h1>
<p>The internet's source of freely-usable images.</p>
<p>Powered by creator everywhere</p>
</div>
<SearchForm
onSubmit={this.handleSubmit}
onChange={this.handleChange}
/>
</div>
</div>
) : (
<div
className="search-bar-container"
style={{ backgroundImage: `url("${photo.urls.full}")` }}
>
<div className="black-layer"></div>
<div className="search-bar-area">
<div className="about-foto-fab">
<h1>Foto-Fab</h1>
<p>The internet's source of freely-usable images.</p>
<p>Powered by creator everywhere</p>
</div>
<SearchForm
onSubmit={this.handleSubmit}
onChange={this.handleChange}
/>
</div>
<div className="picture-info">
<div className="photographer">
<p onClick={this.handleProfile}>
<strong>Photo</strong> by {""}
<strong>{photo.user.name}</strong>
</p>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
photo: state.randomPhotoState.photo,
};
};
export default connect(mapStateToProps, { fetchRandomPhotos })(
withRouter(SearchBar)
);
This is My App.js where all the Routes define Under Router. It's work fine when i jump from one Link to other in those component that are not using redux. but when i click on Redux connected component it's render component but then when i click on any other Link they just change Url Not view.
This is App js File:-
import React, { useEffect, Fragment } from "react";
import { Router, Route, Switch } from "react-router-dom";
import history from "./history";
import Navbar from "./components/layouts/Navbar";
import Landing from "./components/layouts/Landing";
import Profiles from "./components/profiles/Profiles";
import Login from "./components/auth/Login";
import Register from "./components/auth/Register";
import { loadUser } from "./actions/auth";
import { useDispatch } from "react-redux";
const App = () => {
const dispatch = useDispatch(() => loadUser());
useEffect(() => {
dispatch(loadUser());
}, [dispatch]);
return (
<Router history={history}>
<Navbar />
<Route exact path='/' component={Landing} />
<section className='container'>
<Alert />
<Switch>
<Route exact path='/register' component={Register} />
<Route exact path='/login' component={Login} />
<Route path='/profiles' component={Profiles} />
</Switch>
</section>
</Router>
);
};
export default App;
Both Register And LogIn Workimg well when navigating through each other but when I jump to component that using redux profiles, it loads and work but after that when i want to jump from profiles to Register login they just change url not view.
this is My profiles file that using redux and creating issue.
import React, { Fragment, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getProfiles } from "../../actions/profile";
import Spinner from "../layouts/Spinner";
import ProfileItems from "./ProfileItems";
import { withRouter } from "react-router-dom";
const Profiles = () => {
const profile = useSelector(state => state.profile);
const { profiles, loading } = profile;
const dispatch = useDispatch(() => getProfiles());
useEffect(() => dispatch(getProfiles()), [dispatch]);
return (
<Fragment>
{loading ? (
<Spinner />
) : (
<Fragment>
<h1 className='large text-primary'>Developers</h1>
<p className='lead'>
<i className='fab fa-connectdevelop'></i> Browse and Connect With
Developers...
</p>
<div className='profiles'>
{profiles.length > 0 ? (
profiles.map(profile => (
<ProfileItems key={profile._id} profile={profile} />
))
) : (
<h4>profile not Found !!!...</h4>
)}
</div>
</Fragment>
)}
</Fragment>
);
};
export default withRouter(Profiles);
And These are My Login And Register component that are working well when navigate to each other. when they go to profiles after that when i click on link of them they just change urls in address bar but not changing view. It's Login page Register is similar to this..
import React, { Fragment, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { login } from "../../actions/auth";
import { Link, Redirect } from "react-router-dom";
const Login = () => {
const dispatch = useDispatch(() => login());
const isAuthenticated = useSelector(state
=>state.auth.isAuthenticated);
const [formData, setFormData] = useState({
email: "",
password: ""
});
const { email, password } = formData;
const onChange = e => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
const onSubmit = e => {
e.preventDefault();
dispatch(login(email, password));
};
if (isAuthenticated) {
return <Redirect to='/dashboard' />;
}
return (
<Fragment>
<h1 className='large text-primary'>Sign In</h1>
<p className='lead'>
<i className='fas fa-user'>Sign In Your Account!!!</i>
</p>
<form onSubmit={e => onSubmit(e)} className='form'>
<div className='form-group'>
<input
type='email'
name='email'
placeholder='Enter Your Email'
value={email}
onChange={e => onChange(e)}
/>
</div>
<div className='form-group'>
<input
type='password'
name='password'
placeholder='Enter Your Password'
value={password}
onChange={e => onChange(e)}
/>
</div>
<div className='form-group'>
<input type='submit' value='LogIn' className='btn btn-primary' />
</div>
</form>
<p className='my-1'>
Don't have an Account <Link to='/register'>Sign Up</Link>
</p>
</Fragment>
);
};
export default Login;
I searched this alot and mostly got ans use withRouter I tried that one as u can see but still not working or maybe i am not using withRouter on correct component.
I'll do Provide any other information that you need to know about my code if you want and I am using react-redux hooks instead of using connect
I had faced the same problem in the past.
At that time I solve this issue with connected-react-router.
this is how to use the connected-react-router.
how to use connected-react-router