在 ReactJS 中单击按钮状态更改后,React 不会重新呈现(preact)
React doesn't re-render after State change on button click in ReactJS (preact)
我有以下代码可以正常工作,只是它在按下按钮后不会重新呈现页面。
根据用户是否在房间内,我会显示两个不同的按钮。如果用户是房间的一部分,他们可以单击 LEAVE,这将执行 API 调用。然后我希望组件重新加载并让按钮显示 JOIN(因为它们不再是那个房间的一部分)。
import JoinedRooms from '../../components/matrix_joined_rooms';
import JoinRoom from '../../components/matrix_function_join_room';
import LeaveRoom from '../../components/matrix_function_leave_room';
import { useEffect, useState } from 'preact/hooks';
const JoinLeaveButton = ({ name, roomId }) => {
const joinedRooms = JoinedRooms();
const [x, setX] = useState(5);
useEffect(() => console.log("re-render because x changed:", x), [x])
const handleXClick = (number) => {
setX(number)
}
if (joinedRooms.includes(name)) {
return <button name={roomId} onClick={() => {
LeaveRoom(roomId);
handleXClick(10);
}
}>LEAVE</button>
} else {
return <button name={roomId} onClick={() => {
JoinRoom(roomId);
handleXClick(20);
}
}>JOIN</button>
}
}
export default JoinLeaveButton;
我的 JoinRoom 和 LeaveRoom 组件是一个简单的 API 调用,如下所示:
const JoinRoom = (roomId) => {
fetch(`https://example.com/_matrix/client/r0/rooms/${roomId}/join`, {
method: "POST",
headers: {
Authorization: `Bearer ${localStorage.getItem("mx_access_token")}`
}
});
}
export default JoinRoom;
按钮本身的功能有效,问题是我必须手动重新加载页面才能正确显示。
我已经放置了一个虚拟状态,只要您按下按钮就会执行该状态,并且它还会正确地记录到控制台。
我的印象是,更改状态应该在 React(或在本例中为 preact)中重新渲染组件。
谢谢!
本质上:您需要在某处存储已加入房间的状态,并在用户每次加入或离开房间时更新该状态。
我在这里说得太过分了,但是像这样的自定义挂钩很有意义:
// api calls
const fetchRooms = async userid => { ... }
const joinRoom = async (userId,roomId) => { ... }
const leaveRoom = async (userId,roomId) => { ... }
// custom hook
const useRooms = (userId) => {
const [fetching,setFetching] = useState(true);
const [error,setError] = useState(false);
// joinedRooms state is an empty array when this hook is first used
// it will be updated later using the useEffect hook
// or via the join or leave functions below
const [joinedRooms,setJoinedRooms] = useState([]);
// when the component that uses this hook is mounted
// or the user id changes, update the state
useEffect(() => {
let canceled;
setFetching(true);
(async() => {
try {
const rooms = await fetchRooms(userId);
canceled || setJoinedRooms(rooms);
} catch(err) {
canceled || setError(error);
} finally {
canceled || setFetching(false);
}
})();
return () => canceled = true;
},[userId]);
const leave = async roomId => {
try {
await leaveRoom(userId,roomId)
// alternatively you could fetch all the user rooms again here
setJoinedRooms(joined => joined.filter(r => r !== roomId));
} catch(err) {
// couldn't leave the room - what do you want to do with the state?
}
}
const join = async roomId => {
try {
await joinRoom(userId,roomId);
// alternatively you could fetch all the user rooms again here
setJoinedRooms(joined => [...joined,roomId]);
} catch(err) {
// couldn't join the room - what do you want to do with the state?
}
}
return {
fetching,
error,
joinedRooms,
leave,
join
}
}
在组件中,您可以像这样使用它:
const Rooms = (userId,listOfAllYourRoomIds) => {
const { joinedRooms, fetching, error, join, leave } = useRooms(userId);
// the `join` and `leave` functions are what you'll call
// when a user wants to join or leave a room, the joinedRooms prop will get
// updated according, and everything will "just work"
return listOfAllYourRoomIds.map(roomId => <SomeRoomComponent roomId={roomId}/>)
}
我有以下代码可以正常工作,只是它在按下按钮后不会重新呈现页面。
根据用户是否在房间内,我会显示两个不同的按钮。如果用户是房间的一部分,他们可以单击 LEAVE,这将执行 API 调用。然后我希望组件重新加载并让按钮显示 JOIN(因为它们不再是那个房间的一部分)。
import JoinedRooms from '../../components/matrix_joined_rooms';
import JoinRoom from '../../components/matrix_function_join_room';
import LeaveRoom from '../../components/matrix_function_leave_room';
import { useEffect, useState } from 'preact/hooks';
const JoinLeaveButton = ({ name, roomId }) => {
const joinedRooms = JoinedRooms();
const [x, setX] = useState(5);
useEffect(() => console.log("re-render because x changed:", x), [x])
const handleXClick = (number) => {
setX(number)
}
if (joinedRooms.includes(name)) {
return <button name={roomId} onClick={() => {
LeaveRoom(roomId);
handleXClick(10);
}
}>LEAVE</button>
} else {
return <button name={roomId} onClick={() => {
JoinRoom(roomId);
handleXClick(20);
}
}>JOIN</button>
}
}
export default JoinLeaveButton;
我的 JoinRoom 和 LeaveRoom 组件是一个简单的 API 调用,如下所示:
const JoinRoom = (roomId) => {
fetch(`https://example.com/_matrix/client/r0/rooms/${roomId}/join`, {
method: "POST",
headers: {
Authorization: `Bearer ${localStorage.getItem("mx_access_token")}`
}
});
}
export default JoinRoom;
按钮本身的功能有效,问题是我必须手动重新加载页面才能正确显示。
我已经放置了一个虚拟状态,只要您按下按钮就会执行该状态,并且它还会正确地记录到控制台。 我的印象是,更改状态应该在 React(或在本例中为 preact)中重新渲染组件。
谢谢!
本质上:您需要在某处存储已加入房间的状态,并在用户每次加入或离开房间时更新该状态。
我在这里说得太过分了,但是像这样的自定义挂钩很有意义:
// api calls
const fetchRooms = async userid => { ... }
const joinRoom = async (userId,roomId) => { ... }
const leaveRoom = async (userId,roomId) => { ... }
// custom hook
const useRooms = (userId) => {
const [fetching,setFetching] = useState(true);
const [error,setError] = useState(false);
// joinedRooms state is an empty array when this hook is first used
// it will be updated later using the useEffect hook
// or via the join or leave functions below
const [joinedRooms,setJoinedRooms] = useState([]);
// when the component that uses this hook is mounted
// or the user id changes, update the state
useEffect(() => {
let canceled;
setFetching(true);
(async() => {
try {
const rooms = await fetchRooms(userId);
canceled || setJoinedRooms(rooms);
} catch(err) {
canceled || setError(error);
} finally {
canceled || setFetching(false);
}
})();
return () => canceled = true;
},[userId]);
const leave = async roomId => {
try {
await leaveRoom(userId,roomId)
// alternatively you could fetch all the user rooms again here
setJoinedRooms(joined => joined.filter(r => r !== roomId));
} catch(err) {
// couldn't leave the room - what do you want to do with the state?
}
}
const join = async roomId => {
try {
await joinRoom(userId,roomId);
// alternatively you could fetch all the user rooms again here
setJoinedRooms(joined => [...joined,roomId]);
} catch(err) {
// couldn't join the room - what do you want to do with the state?
}
}
return {
fetching,
error,
joinedRooms,
leave,
join
}
}
在组件中,您可以像这样使用它:
const Rooms = (userId,listOfAllYourRoomIds) => {
const { joinedRooms, fetching, error, join, leave } = useRooms(userId);
// the `join` and `leave` functions are what you'll call
// when a user wants to join or leave a room, the joinedRooms prop will get
// updated according, and everything will "just work"
return listOfAllYourRoomIds.map(roomId => <SomeRoomComponent roomId={roomId}/>)
}