【问题标题】:React doesn't re-render after State change on button click in ReactJS (preact)在 ReactJS 中单击按钮时状态更改后,React 不会重新渲染(preact)
【发布时间】:2020-10-08 15:38:04
【问题描述】:

我有以下代码可以正常工作,只是在按下按钮后它不会重新呈现页面。

根据用户是否是房间的一部分,我会显示两个不同的按钮。如果用户是房间的一部分,他们可以单击 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)。

谢谢!

【问题讨论】:

  • 这是因为joinedRooms 没有更新。
  • 我看到您正在尝试在 useEffect 之外进行(异步)API 调用,并且在您的 UseEffect 中没有监听它何时发生变化 - 您正在监听对 [x] 的更改
  • JoinRoomLeaveRoom 都是异步操作,因此您必须等待它们完成,然后才能使用 handleXClick 触发页面重新加载。
  • @secan - 这不是一个有用的评论。 OP 显然是想用 x 状态破解一些东西。
  • 嗨@Adam,你可能是对的,但在阅读别人的代码时,我倾向于尽可能地限制我的假设。我对此事的看法是,除非另有明确声明,否则 OP 有理由按照他们的方式做事。无论如何,非常感谢您的反馈;我会尝试调整我未来的 cmets,以便它们更有用。

标签: javascript reactjs use-state preact


【解决方案1】:

本质上:您需要将已加入房间的状态存储在某处,并在每次用户加入或离开房间时更新该状态

我在这里太过分了,但是像这样的自定义钩子很有意义:

// 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}/>)
   
}

【讨论】:

  • 非常感谢您的回答!不得不承认我还没有完全理解它,但我会努力解决的!
猜你喜欢
  • 2018-11-29
  • 1970-01-01
  • 1970-01-01
  • 2022-11-06
  • 2020-10-03
  • 2021-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多