具有不同道具的同一组件的条件渲染不会在 ReactJS 中卸载组件
conditional rendering of the same component with different props does not unmount the component in ReactJS
我正在开发一个 React Web 应用程序,我遇到的问题是我正在使用不同的道具对同一组件进行条件渲染,但不会卸载它然后再次重新安装它,这意味着该组件挂载周期永远不会再 运行 并且组件永远不会再次初始化
menu === 1 ?
<SidebarChat dataList={chats} title="Chats" />
: menu === 2 ?
<SidebarChat dataList={rooms} title="Rooms" />
: menu === 3 ?
<SidebarChat dataList={users} title="Chats" />
: null
这是代码,我希望 SidebarChat 在菜单更改时卸载而不是装载。
这是 SidebarChat 代码
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;
这是我在其中包含 SidebarChat 的边栏组件
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 我找到了解决方案 !!!!!。
很简单,只需为要有条件地渲染但使用不同道具的组件指定一个键属性,这告诉反应它是一个新组件。
在我的示例中,它是 SidebarChat 组件
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
我正在开发一个 React Web 应用程序,我遇到的问题是我正在使用不同的道具对同一组件进行条件渲染,但不会卸载它然后再次重新安装它,这意味着该组件挂载周期永远不会再 运行 并且组件永远不会再次初始化
menu === 1 ?
<SidebarChat dataList={chats} title="Chats" />
: menu === 2 ?
<SidebarChat dataList={rooms} title="Rooms" />
: menu === 3 ?
<SidebarChat dataList={users} title="Chats" />
: null
这是代码,我希望 SidebarChat 在菜单更改时卸载而不是装载。
这是 SidebarChat 代码
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;
这是我在其中包含 SidebarChat 的边栏组件
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 我找到了解决方案 !!!!!。 很简单,只需为要有条件地渲染但使用不同道具的组件指定一个键属性,这告诉反应它是一个新组件。 在我的示例中,它是 SidebarChat 组件
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