繁体   English   中英

如何使用带有反应钩子的互斥锁?

[英]How can I use a Mutex with a react hook?

我想确保一段代码不会同时运行。 async-Mutex 库似乎对我不起作用。 这是一个最小的复制:

/* eslint-disable */
import React, { useState, useEffect } from "react";
var Mutex = require("async-mutex").Mutex;

const Timer = () => {
  const [isActive, setIsActive] = useState(false);
  const mutex = new Mutex();

  useEffect(() => {
    mutex.runExclusive(async function () {
      console.log("starting", isActive);
      await new Promise((resolve) => setTimeout(resolve, 1000));
      console.log("ending", isActive);
    });
  }, [isActive]);

  return (
    <div className="app">
      <button onClick={() => setIsActive((prev) => !prev)} type="button">
        {isActive ? "Active" : "Inactive"}
      </button>
    </div>
  );
};

export { Timer as default };

密码沙盒

如果你快速点击按钮两次,你可以看到它会打印

starting 
true
starting 
false
starting 
true
ending 
true
ending 
false

你可以看到这些是交错的。

我怎样才能强制它不同时运行?

问题很可能是因为在每次重新渲染时都分配了互斥锁,从而为您提供了一个新实例。

  1. 你可以移动const mutex = new Mutex(); 在您的组件之外,为您的所有 Timer 组件提供一个全局版本,这意味着 Timer 可以相互阻止。

  2. 您可以通过将互斥锁实例包装在React.useMemo钩子中来使互斥锁实例在整个重新渲染过程中保持稳定,这将使每个 Timer 组件都有自己的互斥锁,这意味着 Timer 不能相互阻止,只能阻止自己。

将组件内的声明替换为:

const mutex = React.useMemo(() => new Mutex(), []);

然后在useEffect中将互斥体添加到您的依赖数组中:

useEffect(() => {
  // Your code
}, [isActive, mutex]);

演示

import React, { useState, useEffect, useRef, useMemo} from "react";
var Mutex = require("async-mutex").Mutex;

const Timer = () => {
  const [isActive, setIsActive] = useState(false);
  const mutex = useRef(new Mutex());
  // Or another way, it might be a bit more performant,
  // although the Mutex class has an almost empty constructor
  // const mutex= useMemo(()=> new Mutex(), []);

  useEffect(() => {
    mutex.current.runExclusive(async function () {
      console.log("starting", isActive);
      await new Promise((resolve) => setTimeout(resolve, 1000));
      console.log("ending", isActive);
    });
  }, [isActive]);

  return (
    <div className="app">
      <button onClick={() => setIsActive((prev) => !prev)} type="button">
        {isActive ? "Active" : "Inactive"}
      </button>
    </div>
  );
};

export { Timer as default };

如果您想取消之前的 function 调用并支持卸载时的异步例程清理( 演示):

/* eslint-disable */
import React, { useState, useEffect } from "react";
import CPromise from "c-promise2";
import { useAsyncEffect, useAsyncCallback } from "use-async-effect2";

export default function Timer() {
  const [isActive, setIsActive] = useState(false);

  const callback = useAsyncCallback(
    function* () {
      console.log("starting", isActive);
      yield CPromise.delay(1000);
      console.log("ending", isActive);
    },
    { combine: true }
  );

  useAsyncEffect(
    function* () {
      console.log("mount");
      yield callback();
    },
    [isActive]
  );

  return (
    <div className="app">
      <button onClick={() => setIsActive((prev) => !prev)} type="button">
        {isActive ? "Active" : "Inactive"}
      </button>
    </div>
  );
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM