为什么在我的代码中 UseState 有时有效有时无效

How come UseState sometimes works sometimes doesn't in my code

我从 React useEffect 和 useState 挂钩中得到一个奇怪的行为:10 次中有 7-8 次它没有设置保险库并且它一直有一个空对象,而 10 次中有 2-3 次它设置了它。

我从 binance testnet 区块链中正确获取了一些数据(控制台日志每次都记录正确的数据,永远不会未定义)但是当我使用 useState 将数据保存到保险库中时,有时它会将其保存到保险库中,有时不会。

不知何故,有时(很少次)SetVault 工作并使用区块链的结果设置保险库,有时(大多数时候)它只是不设置任何东西。

知道发生了什么吗?

代码:

 const [vaults, setVaults] = useState({});

 useEffect(() => {
    async function loadBloackchainData() {
      const web3 = new Web3('https://data-seed-prebsc-1-s1.binance.org:8545/');
      const vault = new web3.eth.Contract(VaultABI, testnet.Vaults[0].address);
      const name = await vault.methods.symbol().call();
      console.log('name: ', name);  // ok data is logged correctly
      const totalSupply = parseInt(await vault.methods.totalSupply().call());
      console.log('totalSupply: ', totalSupply); // ok data is logged correctly
      const totalBorrowed = parseInt(await vault.methods.vaultDebtVal().call());
      console.log('totalBorrowed: ', totalBorrowed); // ok data is logged correctly
      const capUtilRate = Number.isNaN(totalSupply / totalBorrowed) ? 0 : totalSupply / totalBorrowed;
      console.log('capUtilRate: ', capUtilRate); // ok data is logged correctly

      // ALL GOOD TILL HERE

      setVaults(vaults => ({
        ...vaults,
        [name]: {
           totalSupply,
           totalBorrowed,
           capUtilRate,
        },
      }));
      console.log('vaults inside useffect: ', vaults); // {}
    }

    loadBloackchainData();
  }, []);
  console.log('vaults outside useffect: ', vaults); // {}

setVaults 具有异步逻辑。我说逻辑是因为它没有 return 任何 Promise。调用setVaults后,它确实会更新状态,但之后控制台记录时,您可能不会注意到它。

在它下面创建另一个 useEffect 以检查它是否正在被更改。

useEffect(() => {
    console.log('Updated Vaults: ', vaults);
}, [vaults]);

在 React 中设置状态是一个异步过程,这意味着不能保证您在更新状态后立即获得可用的更新值。

以上可能是您无法获得 console.log('vaults inside useffect: ', vaults); 中的值的原因。

可能导致此问题的另一件事是您在两个不同的地方使用了相同的名称。即

const [vaults, setVaults] = useState({});

&

setVaults(vaults => ({
   ...vaults,
   [name]: {
      totalSupply,
      totalBorrowed,
      capUtilRate,
   },
}));

在上面的代码中,您使用了相同的名称 vaults,这可能会有所不同。

尝试将代码更改为以下代码,看看是否有效。

setVaults(prevVaults => ({
   ...prevVaults,
   [name]: {
      totalSupply,
      totalBorrowed,
      capUtilRate,
   },
}));

设置状态变量不会像您在这里期望的那样立即运行。代码的执行顺序如下:

const [vaults, setVaults] = useState({});

将状态变量库初始化为 {}

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

useEffect内的回调已注册,但不会立即运行。

console.log('vaults outside useffect: ', vaults);

由于初始值,这将现在打印 {}

如果你 return 一些 JSX,它现在会被渲染。

之后会触发useEffect回调

async function loadBloackchainData() {
      ...
}
loadBloackchainData();

loadBloackchainData 函数调用中,它按预期工作,直到出现 // ALL GOOD TILL HERE 注释。

setVaults(vaults => ({
        ...vaults,
        [name]: {
           totalSupply,
           totalBorrowed,
           capUtilRate,
        },
      }));
console.log('vaults inside useffect: ', vaults); // {}

setVaults 调用不会立即更新值 vaults,而是 React 将此类更新批处理到状态,稍后会 运行。所以 console.log 将为 vaults 打印 {} 的当前值。 如果你希望 console.log 具有更新状态的值,也许 运行 一些逻辑,你可以尝试:

setVaults(vaults => {
  const updatedState = ({
        ...vaults,
        [name]: {
           totalSupply,
           totalBorrowed,
           capUtilRate,
        },
  });
  console.log('vaults inside useffect: ', updatedState.vaults);
  return updatedState });

setVaults 是一个异步操作,它不会立即更新,要观察变化使用 useEffect 和 vaults 的依赖关系。

const [vaults, setVaults] = useState({});
async function loadBloackchainData() {
  const web3 = new Web3("https://data-seed-prebsc-1-s1.binance.org:8545/");
  const vault = new web3.eth.Contract(VaultABI, testnet.Vaults[0].address);
  const name = await vault.methods.symbol().call();
  console.log("name: ", name); // ok data is logged correctly
  const totalSupply = parseInt(await vault.methods.totalSupply().call());
  console.log("totalSupply: ", totalSupply); // ok data is logged correctly
  const totalBorrowed = parseInt(await vault.methods.vaultDebtVal().call());
  console.log("totalBorrowed: ", totalBorrowed); // ok data is logged correctly
  const capUtilRate = Number.isNaN(totalSupply / totalBorrowed)
    ? 0
    : totalSupply / totalBorrowed;
  console.log("capUtilRate: ", capUtilRate); // ok data is logged correctly

  // ALL GOOD TILL HERE

  setVaults((prev) => ({
    ...prev,
    [name]: {
      totalSupply,
      totalBorrowed,
      capUtilRate,
    },
  }));
  console.log("vaults inside useffect: ", vaults); // {}
}

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

useEffect(() => {
  console.log("vaults outside useffect: ", vaults); // First render {}, after setVaults you will get object
}, [vaults]);