react函数里的state不更新,咋办?

场景

在这里插入图片描述
有一个聊天输入框组件,输入框上面有表情包组件。
通过redux创建了store,存储一个message的状态,用于表情包和输入框共享状态。
输入框通过设置value和onClick做了一个简单双向绑定,其中value的值为store里的message。

import { update } from "@/features/messageSlice";
import { useStoreSelector, useStoreDispatch } from "@/hooks/store";
const ChatInput: React.FC = () => {
  const message = useStoreSelector((state) => state.message.value);
  const dispatch = useStoreDispatch();
  return (
    <textarea
      value={message}
      onChange={(e) => {
        const val = (e.target as HTMLTextAreaElement).value;
        dispatch(update(val));
      }}
      className="chatInput"
      style={{
        width: "100%",
        resize: "none",
        outline: "none",
        border: "none",
      }}
    ></textarea>
  );
};
export default ChatInput;

表情包组件做了一个方法,选择表情包时通过回调传回值与message值做拼接,结果message值为上一次的值

import { useStoreSelector, useStoreDispatch } from "@/hooks/store";
import { update } from "@/features/messageSlice";
import DIcon from "@/components/DIcon";
import { chatTools } from "../../config";
import { Tooltip } from "antd";
const ChatTools: React.FC = () => {
  const message = useStoreSelector((state) => state.message.value);
  const dispatch = useStoreDispatch();
  const onSelectContent = (ct: any) => {
    dispatch(update(message + ct.emoji));
  };
  return (
    <div className="chatTools">
      {chatTools.map(({ icon, tooltip, extra }) => {
        const Comp: any = extra;
        return (
          <Tooltip key={icon} placement="top" title={tooltip}>
            <Tooltip
              placement="top"
              overlayStyle={{ maxWidth: "100%" }}
              overlayInnerStyle={{ color: "#000000" }}
              color="#ffffff"
              title={<Comp onSelectContent={onSelectContent}></Comp>}
              trigger="click"
            >
              <span style={{ margin: "0 12px 0 0" }}>
                <DIcon type={icon} style={{ fontSize: "24px" }}></DIcon>
              </span>
            </Tooltip>
          </Tooltip>
        );
      })}
    </div>
  );
};

export default ChatTools;

结果:当我输入一段文字后,添加一个表情包,再输入一段文字,再添加一个表情包时。。就会覆盖掉前一段文字。

个人尝试:
1.设置一个state存储选择的表情包,然后通过useEffect监听state的变化更新message。结果出现新bug,那就是不能重复选择表情包,因为state不改变导致useEffect不执行。
2.仔细分析了一下原因,是Tooltip组件未更新,导致message是属于第一次闭包的。在内嵌的Tooltips添加了一个key,值为message。结果成功,但是有个警告
在这里插入图片描述

阅读 358
avatarAI BotBETA

问题出在于你的 Redux 状态更新是异步的,但是你在 onSelectContent 方法中试图立即使用新的状态值。当你在 Redux 中派发一个 action 时,状态并不会立即更新,而是在下一个渲染周期中更新。因此,你在 onSelectContent 方法中使用的 message 值仍然是旧的,而不是你期望的新值。

要解决这个问题,你可以使用 Redux 的 useSelector 钩子的第二个参数,这是一个依赖数组。当这个数组中的任何值发生变化时,useSelector 会重新运行,并返回新的值。在你的情况下,你可以将 message 添加到这个数组中,这样每当 message 更新时,useSelector 都会返回新的值。

另外,关于你提到的警告,这个警告是由于你在 Tooltip 组件中使用了不稳定的 key。在你的情况下,message 是一个可能会频繁变化的值,所以用它作为 key 是不稳定的,这可能会导致性能问题和渲染错误。

下面是修改后的代码:

import { useStoreSelector, useStoreDispatch } from "@/hooks/store";
import { update } from "@/features/messageSlice";
import DIcon from "@/components/DIcon";
import { chatTools } from "../../config";
import { Tooltip } from "antd";
const ChatTools: React.FC = () => {
  const dispatch = useStoreDispatch();
  const [message, setMessage] = React.useState("");

  React.useEffect(() => {
    setMessage(useStoreSelector((state) => state.message.value));
  }, []); // 监听 Redux store 中的 message 变化

  const onSelectContent = (ct: any) => {
    dispatch(update(message + ct.emoji));
  };

  return (
    <div className="chatTools">
      {chatTools.map(({ icon, tooltip, extra }) => {
        const Comp: any = extra;
        return (
          <Tooltip key={icon} placement="top" title={tooltip}>
            <Tooltip
              placement="top"
              overlayStyle={{ maxWidth: "100%" }}
              overlayInnerStyle={{ color: "#000000" }}
              color="#ffffff"
              title={<Comp onSelectContent={onSelectContent}></Comp>}
              trigger="click"
            >
              <span style={{ margin: "0 12px 0 0" }}>
                <DIcon type={icon} style={{ fontSize: "24px" }}></DIcon>
              </span>
            </Tooltip>
          </Tooltip>
        );
      })}
    </div>
  );
};

export default ChatTools;

在这个修改后的版本中,我创建了一个新的 state message 来存储从 Redux store 中获取的消息。然后,我使用 useEffect 来监听 Redux store 中的 message 变化,并在变化时更新这个 state。这样,你就可以在 onSelectContent 方法中使用这个稳定的 message 值了。

同时,我也移除了 Tooltip 组件中的 key 属性,因为使用不稳定的值作为 key 可能会导致性能问题和渲染错误。由于你的 Tooltip 组件的 key 是基于图标的,这应该是稳定的,所以移除 key 属性应该是安全的。

希望这个解答能帮到你!如果你还有其他问题,欢迎继续提问。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Microsoft
子站问答
访问
宣传栏