I'm working on a react web application the problem that I have is that I'm doing a conditional rendering of the same component with different props but that does not unmount it then remounts it again which means that the component did mount cycle is never run again and the component is never initialized again
menu === 1 ?
<SidebarChat dataList={chats} title="Chats" />
: menu === 2 ?
<SidebarChat dataList={rooms} title="Rooms" />
: menu === 3 ?
<SidebarChat dataList={users} title="Chats" />
: null
this is the code and i want SidebarChat to unmounts than mounts when menu changes.
this is SidebarChat code
import React, { useEffect, useState } from 'react';
import { Avatar, IconButton } from '@material-ui/core';
import { Add } from '@material-ui/icons';
import './SidebarChat.css';
import db from './firebase';
import { Link } from 'react-router-dom';
function SidebarChat({ dataList, title }) {
const [messages, setMessages] = useState([]);
const createChat = () => {
const roomName = prompt("Please enter name for chat");
if (roomName) {
//Do some clever database stuff right here .....
db.collection("rooms").add({
name: roomName,
})
}
}
useEffect(() => {
console.log("first mount of " + title)
}, [])
useEffect(() => {
if (dataList) {
console.log(dataList);
var unsubscribe = dataList.map((data, i) => {
return
db.collection("rooms").doc(data.id).collection("messages").orderBy("timestamp",
"desc").onSnapshot(snap => {
if (snap.docs[0]) {
console.log(snap.docs[0].data())
setMessages(message => {
const arr = [...message];
arr[i] = snap.docs[0].data().message;
return arr;
})
}
})
});
//console.log(messages);
}
return () => {
if (unsubscribe) {
unsubscribe.forEach(sub => sub())
}
}
}, [dataList])
console.log(title)
console.log(messages)
return (
<div className="sidebar__chat--container">
<div className="sidebar__chat--addRoom" onClick={createChat}>
<IconButton >
<Add />
</IconButton>
</div>
{dataList && messages.length > 0 ?
<React.Fragment>
<h2>{title} </h2>
{dataList.map((data, i) => data ?
<Link key={data.id} to={{
pathname: `/room/${data.id}`,
state: {
photoURL: `${data.photoURL ? data.photoURL :
`https://avatars.dicebear.com/api/human/${data.id}.svg`}`,
name: data.name,
}
}} >
<div className="sidebar__chat">
<Avatar src={`${data.photoURL ? data.photoURL :
`https://avatars.dicebear.com/api/human/${data.id}.svg`}`} />
<div className="sidebar__chat--info">
<h2>{data.name} </h2>
<p>{messages[i]}</p>
</div>
</div>
</Link> :
null
)}
</React.Fragment>
: null
}
</div>
)
}
export default SidebarChat;
and this is the Sidebar Component where I include SidebarChat in it
import React, { useEffect, useState } from 'react';
import SidebarChat from './SidebarChat';
import { Avatar, IconButton } from '@material-ui/core';
import { Message, PeopleAlt, Home, ExitToApp as LogOut, SearchOutlined, DonutLarge as DonutLargeIcon,
Chat as ChatIcon } from '@material-ui/icons';
import db, { auth } from "./firebase";
import { useStateValue } from './StateProvider';
import { NavLink, Route, Switch, Link } from 'react-router-dom'
import './Sidebar.css';
const height = window.innerHeight;
if (window.innerWidth > 760) {
var Nav = (props) =>
<div className={`${props.classSelected ? "sidebar__menu--selected" : ""}`} onClick=
{props.click}>
{props.children}
</div>
} else {
var Nav = NavLink;
}
function Sidebar() {
const [rooms, setRooms] = useState([]);
const [users, setUsers] = useState([]);
const [chats, setChats] = useState([]);
const [menu, setMenu] = useState(1);
const [{ user }] = useStateValue();
useEffect(() => {
const unsubscribe1 = db.collection("rooms").onSnapshot(snapshot => {
setRooms(
snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
)
})
const unsubscribe2 = db.collection("users").orderBy("timestamp", "desc").onSnapshot(snap => {
const arr = [];
snap.docs.forEach(doc => {
if (doc.id !== user.uid) {
arr.push({
...doc.data(),
id: doc.id > user.uid ? doc.id + user.uid : user.uid + doc.id,
})
}
});
setUsers(arr);
});
const unsubscribe3 = db.collection("users").doc(user.uid).collection("chats").orderBy("timestamp", "desc").onSnapshot(snap => {
console.log(snap.docs);
Promise.all(snap.docs.map(doc => {
return db.collection("rooms").doc(doc.id).get();
})).then(rooms => {
console.log(rooms);
const chat = rooms.map((room, i) => ({
id: room.id,
photoURL: snap.docs[i].data().photoURL,
name: snap.docs[i].data().name,
...room.data(),
}));
console.log(chat);
setChats(chat);
})
})
return () => {
unsubscribe1();
unsubscribe2();
unsubscribe3();
}
}, []);
return (
<div className="sidebar" style={{
minHeight: window.innerWidth <= 760 ? height : "auto"
}}>
<div className="sidebar__header">
<Avatar src={user?.photoURL} />
<div className="sidebar__header--right">
<IconButton>
<DonutLargeIcon />
</IconButton>
<IconButton>
<ChatIcon />
</IconButton>
<IconButton onClick={() => auth.signOut()} >
<LogOut />
</IconButton>
</div>
</div>
<div className="sidebar__search">
<div className="sidebar__search--container">
<SearchOutlined />
<input placeholder="Search or start new chat" type="text" />
</div>
</div>
<div className="sidebar__menu">
<Nav
classSelected={menu === 1 ? true : false}
to="/"
click={() => setMenu(1)}
exact
activeClassName="sidebar__menu--selected"
>
<div className="sidebar__menu--home">
<Home />
<div className="sidebar__menu--line"></div>
</div>
</Nav>
<Nav
classSelected={menu === 2 ? true : false}
to="/rooms"
click={() => setMenu(2)}
activeClassName="sidebar__menu--selected"
>
<div className="sidebar__menu--rooms">
<Message />
<div className="sidebar__menu--line"></div>
</div>
</Nav>
<Nav
classSelected={menu === 3 ? true : false}
to="/users"
click={() => setMenu(3)}
activeClassName="sidebar__menu--selected"
>
<div className="sidebar__menu--users">
<PeopleAlt />
<div className="sidebar__menu--line"></div>
</div>
</Nav>
</div>
{window.innerWidth <= 760 ?
<>
<Route path="/users" exact >
<SidebarChat dataList={users} title="Users" />
</Route>
<Route path="/" exact >
<>
{/*
<h2>Chats</h2>
<SidebarChat addNewChat />
{chats.map(room => room ? <SidebarChat key={room.id} id={room.id} name={room.name} photo={room.photoURL} /> : null)} */}
<SidebarChat dataList={chats} title="Chats" />
</>
</Route>
<Route path="/rooms" exact >
<>
<SidebarChat dataList={rooms} title="Rooms" />
</>
</Route>
</>
:
menu === 1 ?
<SidebarChat dataList={chats} title="Chats" />
: menu === 2 ?
<SidebarChat dataList={rooms} title="Rooms" />
: menu === 3 ?
<SidebarChat dataList={users} title="Chats" />
: null
}
</div>
);
};
export default Sidebar;
Guuuuuuuuuys I found the solution !!!!!. It's simple just specify a key attribute to The component you want to render conditionally but with different props, this tells react that it's a new component. In my example it's the SidebarChat component
menu === 1 ?
<SidebarChat key="chats" dataList={chats} title="Chats" />
: menu === 2 ?
<SidebarChat key="rooms" dataList={rooms} title="Rooms" />
: menu === 3 ?
<SidebarChat key="users" dataList={users} title="Users" />
: null