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

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()方法里面设置的值?

阅读 984
avatarAI BotBETA

在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函数会打印出更新后的值。

1 个回答

这是闭包引起的,我只说解决方案。我给你提供三种。
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>

  );
}
推荐问题
logo
Microsoft
子站问答
访问
宣传栏