当前位置: 首页 > 知识库问答 >
问题:

react.js - 如何在React的Modal.confirm后同步更新状态并获取?

汤飞翮
2024-07-15

react,antd,typescript
为什么在Modal.confirm后,前面update()方法设置的值没有成功,在getRow()方法里面获取的还是原来的值

import { Button, Modal } from 'antd';
import React, { useState, useEffect, useRef } from 'react';
function PageComponent() {
  const [person, setPerson] = useState({
    age: "1",
    name: "张三"
  });

  const msgDialog  = async (msg: string, bShowCanBtn: boolean = true):Promise<{modal : ReturnType<typeof Modal.confirm> , status : true | false}> => {
    return new Promise((resolve, reject) => {
      const modal =  Modal.confirm({
        title: "test",
        content: <h4>{msg}</h4>,
        closable: true,
        okText: "确定",
        cancelText: "取消",
        cancelButtonProps: { style: { display: bShowCanBtn == false ? "none" : "inline" } },
        centered: true,
        onOk() {
          resolve({modal , status : true});
        },
        onCancel() {
          resolve({modal , status : false});
        },
      })
    })
  };
  const cmd_update = async () => {
    let instance = await msgDialog('是否更新?')
    if(!instance?.status) {
      instance?.modal.destroy();
      return;
    }
    await update();
    let retInstance = await msgDialog('成功登录!', false);
    if(!retInstance?.status) {
      retInstance?.modal.destroy();
      return;
    }
    await getRow();
  }
  const update  = async () => {
    const obj = {age: "2", name: "李四"};
    setPerson((form) => ({ ...form, ...obj }));
  };
  const getRow  = async () => {
    console.info("getRow=", person.age, person.name)
    
  };
  useEffect(() => {
    console.info("useEffect=", person.age, person.name)
    getRow();
  }, [person.age, person.name]);

  return (
    <div>
      <Button onClick={cmd_update}>更    新</Button>
    </div>
    
  );
}
export default PageComponent;

怎么样才能在getRow方法里面获取update()方法里面设置的值?

共有2个答案

小牛23172
2024-07-15

这是闭包引起的,我只说解决方案。我给你提供三种。
1: 是使用 useEffect

function PageComponent() {
  const [person, setPerson] = useState({
    age: "1",
    name: "张三"
  });

  const msgDialog = async (msg: string, bShowCanBtn: boolean = true): Promise<{ modal: ReturnType<typeof Modal.confirm>, status: true | false }> => {
    return new Promise((resolve, reject) => {
      const modal = Modal.confirm({
        title: "test",
        content: <h4>{msg}</h4>,
        closable: true,
        okText: "确定",
        cancelText: "取消",
        cancelButtonProps: { style: { display: bShowCanBtn == false ? "none" : "inline" } },
        centered: true,
        onOk() {
          resolve({ modal, status: true });
        },
        onCancel() {
          resolve({ modal, status: false });
        },
      })
    })
  };
  const cmd_update = (async () => {
    let instance = await msgDialog('是否更新?')
    if (!instance?.status) {
      instance?.modal.destroy();
      return;
    }
    await update();
    // let retInstance = await msgDialog('成功登录!', false);
    // if (!retInstance?.status) {
    //   retInstance?.modal.destroy();
    //   return;
    // }
    // await getRow();
  })
  const update = async () => {
    const obj = { age: "2", name: "李四" };
    setPerson((form) => ({ ...form, ...obj }));
  };
  const getRow = () => {
    console.info("getRow=", person.age, person.name)

  }

  useEffect(() => {
    //  更新后的逻辑放在 useEffect中  
    async function next() {
      let retInstance = await msgDialog('成功登录!', false);
      if (!retInstance?.status) {
        retInstance?.modal.destroy();
        return;
      }
      await getRow();
    }
    next();
  }, [person]);

  return (
    <div>
      <Button onClick={cmd_update}>更    新</Button>
    </div>

  );
}

2:更新函数返回 更新后的状态

function PageComponent() {
  const [person, setPerson] = useState({
    age: "1",
    name: "张三"
  });

  const msgDialog = async (msg: string, bShowCanBtn: boolean = true): Promise<{ modal: ReturnType<typeof Modal.confirm>, status: true | false }> => {
    return new Promise((resolve, reject) => {
      const modal = Modal.confirm({
        title: "test",
        content: <h4>{msg}</h4>,
        closable: true,
        okText: "确定",
        cancelText: "取消",
        cancelButtonProps: { style: { display: bShowCanBtn == false ? "none" : "inline" } },
        centered: true,
        onOk() {
          resolve({ modal, status: true });
        },
        onCancel() {
          resolve({ modal, status: false });
        },
      })
    })
  };
  const cmd_update = (async () => {
    let instance = await msgDialog('是否更新?')
    if (!instance?.status) {
      instance?.modal.destroy();
      return;
    }
    const assignPerson = await update();
    let retInstance = await msgDialog('成功登录!', false);
    if (!retInstance?.status) {
      retInstance?.modal.destroy();
      return;
    }
    await getRow();
  })

  // update函数更新完返回 最新的状态
  const update = async () => {
    const obj = { age: "2", name: "李四" };
    return new Promise((resolve) => {
      setPerson((form) => {
        const assignPerson = { ...form, ...obj };
        resolve(assignPerson);
        return assignPerson;
      });
    })
  };
  const getRow = () => {
    console.info("getRow=", person.age, person.name)

  }

  return (
    <div>
      <Button onClick={cmd_update}>更    新</Button>
    </div>

  );
}

3: 利用 useRef 这个我不多赘述了,不怎么推荐。

你的这个弹框业务的复杂度不高的话 可以使用 update 不用重复生成弹框实例

function PageComponent() {
  const [person, setPerson] = useState({
    age: "1",
    name: "张三"
  });

  const msgDialog = async (msg: string, bShowCanBtn: boolean = true): Promise<{ modal: ReturnType<typeof Modal.confirm>, status: true | false }> => {
    return new Promise((resolve) => {
      const modal = Modal.confirm({
        title: "test",
        content: <h4>{msg}</h4>,
        // closable: true,
        okText: "确定",
        cancelText: "取消",
        cancelButtonProps: { style: { display: "inline" } },
        // centered: true,
        okCancel: false,
        onOk: () => {
          resolve({ modal, status: true });
          //  组织关闭弹框
          return true;
        },
        onCancel: () => {
          resolve({ modal, status: false });
        },
      })
    })
  };
  const cmd_update = async () => {
    let instance = await msgDialog('是否更新?')
    if (!instance?.status) {
      instance?.modal.destroy();
      return;
    }
    console.log('等待点击是');

    //  点击是
    instance.modal.update({
      okCancel: false,
      content: <h4>成功登录!</h4>,
      cancelButtonProps: {
        style: { display: "none" }
      },
      okButtonProps: {
        onClick: () => {
          instance.modal.destroy();
        }
      }
    })

    console.log(instance);
  }

  return (
    <div>
      <Button onClick={cmd_update}>更    新</Button>
    </div>

  );
}
梁丘琛
2024-07-15

在React中,状态的更新是异步的,这意味着setPerson函数在调用后不会立即更新person状态的值。相反,React会安排一次状态更新,并在下一个渲染周期中反映这个变化。因此,直接在调用setPerson之后尝试读取person状态的值可能仍然会得到旧的值。

在你的代码中,getRow函数是在update函数调用后立即调用的,但由于状态的异步更新特性,此时person状态可能还没有更新。然而,你已经通过useEffect钩子正确地设置了依赖项,以便在person状态发生变化时重新运行getRow

但是,在cmd_update函数中,你尝试在updategetRow之间立即同步地等待状态更新,这是不可能的。你应该依赖useEffect来在状态更新后重新运行getRow,而不是在cmd_update函数中直接调用它。

不过,如果你想在cmd_update函数中确保getRowupdate之后调用,并且确保使用的是更新后的person状态值,你可以使用useRef来保存一个对person的引用,并在update函数中更新它。但请注意,这并不是一个好的实践,因为它会绕过React的状态管理。更好的方法是依赖于React的状态更新机制,并使用useEffect钩子来监听状态变化。

然而,在你的例子中,你可以简单地从useEffect钩子中删除getRow的调用,因为它已经在状态更新时自动运行了。另外,你可以删除getRow函数中的await关键字,因为它不是异步函数,也不应该被当作异步函数使用。

修改后的代码如下:

// ...
useEffect(() => {
  console.info("useEffect=", person.age, person.name);
  // 这里已经足够,不需要在cmd_update中直接调用getRow
}, [person]);

const cmd_update = async () => {
  let instance = await msgDialog('是否更新?');
  if (!instance?.status) {
    instance?.modal.destroy();
    return;
  }
  await update();
  // 这里不需要再调用getRow,因为useEffect会在person状态更新后自动运行它
  let retInstance = await msgDialog('成功登录!', false);
  if (!retInstance?.status) {
    retInstance?.modal.destroy();
    return;
  }
  // 如果需要在cmd_update之后立即执行某些操作,并且这些操作依赖于新的person状态,
  // 你应该考虑将这些操作放在另一个useEffect钩子中,或者重新设计你的组件逻辑。
};

// ...

const getRow = () => {
  // 不再是异步函数,也不需要await
  console.info("getRow=", person.age, person.name);
};

// ...

这样,每当person状态更新时,useEffect都会运行,并且getRow函数会打印出更新后的值。

 类似资料:
  • 问题内容: 我的结构如下所示: 组件3应该根据组件5的状态显示一些数据。由于道具是不可变的,我不能简单地将其状态保存在组件1中并转发它,对吗?是的,我已经阅读了有关redux的内容,但不想使用它。我希望有可能通过反应来解决。我错了吗? 问题答案: 对于孩子与父母的通信,您应该传递一个将状态从父母设置为孩子的函数,如下所示 这样,孩子可以通过调用通过props传递的函数来更新父母的状态。 但是您将不

  • 用的是 React 和 Antd v5 为什么在改了 state 之后元素还是没变化呢 问过 GPT 但是没有结果

  • 假设,我有如下 concurrentHashMap 代码: 此 方法是从多个线程调用的,可以尝试同时更新同一 accountId 的数量。 如何确保currentBalance在< code>get和< code>put之间不发生变化?因为根据我的理解,如果线程在执行< code>get后抢占,同时其他线程更新余额,< code>put将使用旧余额执行更新。

  • 问题内容: 我正在尝试仅更新数组状态中的一个元素,但不确定如何执行此操作。 State 设定状态 如果我想更改标记的第四个元素中的键,该怎么做? 谢谢 问题答案: 重要的一点是,我们不应该直接更改状态数组,而要始终在新数组中进行更改,然后使用setState更新状态值。 如 Doc 建议: 切勿直接更改this.state,将this.state视为不可变。 更新状态数组的基本流程是: 1- 首先

  • 问题内容: 我试图通过使用这样的嵌套属性来组织我的状态: 但是像这样更新状态 不起作用。如何正确完成? 问题答案: 为了创建嵌套对象,您可以按照以下方法进行操作,因为我认为setState无法处理嵌套更新。 这个想法是创建一个虚拟对象,对其执行操作,然后用更新的对象替换组件的状态 现在,散布运算符仅创建对象的一级嵌套副本。如果您的状态是高度嵌套的,例如: 您可以在每个级别使用传播运算符来设置状态,

  • 所以我有这个: 只是一个数组的数字例如然而没有给出正确的总数,但?我甚至放了一个超时延迟,看看这是否解决了问题。任何明显的还是我应该发布更多的代码?

  • 使用最新iOS设备中的M7芯片,当用户使用CMMotionActivityManager从静止状态转到跑步、行走等状态时,可以通过编程方式获得通知。Stava和Runkeeper都使用此功能在检测到用户未通过M7移动时自动暂停GPS轮询(关闭GPS天线),然后在再次移动时重新启用GPS更新。它可以在应用程序处于后台状态时执行此操作,这是此处的关键。 复制此功能时遇到的问题是,如果我在应用程序处于后