【问题标题】:React hook state not being updated反应钩子状态未更新
【发布时间】:2020-11-12 08:50:07
【问题描述】:

在我的应用程序中,我有一个 RoomsContainer 父组件,它有一个 Room 子组件数组。在Room 中,我希望有一个按钮,可以通过调用父组件的addRoom 函数向RoomsContainer 添加额外的Room 元素。

这就是我所拥有的:

function RoomsContainer() {
  const [rooms, setRooms] = useState([]);

  const addRoom = () => {
    var uniqid = require('uniqid');
    let key = uniqid("room-");
    setRooms([
      ...rooms,
      <Room
        key={key}
        id={key}
        addRoom={addRoom}
        removeRoom={removeRoom}
      />
    ]);
    console.log(rooms);
  }

  useEffect(() => {
    addRoom();
  }, []);

  // ...

  return (
    <div id="room-wrapper">
      {rooms}
    </div>
  )
}

function Room(props) {
  // ...
  return (
    <button onClick={props.addRoom}>
      Add room
    </button>
  )
}

这会在页面加载时添加第一个房间,但是当我单击 Room 组件中的 Add Room 按钮时,它不会添加任何其他房间。单击它似乎还会使用新的Room 元素完全重新渲染整个RoomsContainer 组件,而不是修改其状态。同样console.log(rooms) 总是显示一个空数组,即使在设置之后也是如此。

【问题讨论】:

  • 这能回答你的问题吗? state is not being updated when using React Hooks
  • @SergedeGossondeVarennes 我看不出这个问题是如何重复的。我没有使用React.Fragment,我没有使用map,并且我将一个空的第二个参数传递给useEffect。能不能说的具体点?
  • 对不起。读错了!

标签: reactjs react-hooks state


【解决方案1】:

您的 addRoom 函数中有一个 stale closure,我认为您不应该将 jsx 保存到本地状态。以下应该有效:

const id = ((id) => () => ++id)(0);

function RoomsContainer() {
  const [rooms, setRooms] = React.useState([]);
  //not sure why you want to use require in a react app
  //var uniqid = require('uniqid');

  //use React.useCallback so this function never changes
  const addRoom = React.useCallback(
    //setRooms takes a callback function that receives the current rooms
    //  only store the id in rooms, not the jsx
    () => setRooms((rooms) => [...rooms, id()]),
    []
  );
  const removeRoom = React.useCallback(
    //setRooms takes a callback function that receives the current rooms
    //  only store the id in rooms, not the jsx
    (id) =>
      setRooms((rooms) =>
        rooms.filter((room) => room !== id)
      ),
    []
  );

  React.useEffect(() => {
    addRoom();
  }, [addRoom]);

  return (
    // do not use id in html elements, id should be unique
    // in the DOM but you can't/shouldn't guarantee that your app doesn't
    // have multiple RoomContainer elements
    <ul>
      {rooms.map((id) => (
        <Room
          key={id}
          id={id}
          addRoom={addRoom}
          removeRoom={removeRoom}
        />
      ))}
    </ul>
  );
}
//make Room a pure component so there are no needless re renders
const Room = React.memo(function Room({
  addRoom,
  removeRoom,
  id,
}) {
  return (
    <li>
      <button onClick={addRoom}>Add room</button>
      <button onClick={() => removeRoom(id)}>
        Remove room {id}
      </button>
    </li>
  );
});

ReactDOM.render(
  <RoomsContainer />,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

【讨论】:

  • 很好的答案。谢谢。
【解决方案2】:

console.log(rooms);初始显示一个空数组是正常的,因为设置状态是异步的。

这是你应该做的:

function RoomsContainer() {
  const [rooms, setRooms] = useState([]);

  const addRoom = useCallback(() => {
    var uniqid = require("uniqid");
    let key = uniqid("room-");
    setRooms(rooms => [...rooms, key]);
  }, []);

  useEffect(() => {
    addRoom();
  }, []);

  return (
    <div id="room-wrapper">
      {rooms.map((key) => {
        return <Room key={key} id={key} addRoom={addRoom} />;
      })}
    </div>
  );
}

【讨论】:

  • 如果你使用 useCallback 你不应该让它不必要地依赖于rooms,因为它会在每次rooms更改并重新渲染列表中的所有项目时不必要地重新创建函数。如果您设置如下状态,则可以从依赖项中删除 roomssetRooms(rooms=&gt;[...rooms, key]);
猜你喜欢
  • 2020-09-24
  • 1970-01-01
  • 1970-01-01
  • 2020-04-19
  • 2021-05-19
  • 1970-01-01
  • 1970-01-01
  • 2020-03-10
  • 1970-01-01
相关资源
最近更新 更多