javascript 在React中,如何禁用和启用子组件中的按钮,并将onClick绑定到父组件的回调函数?

pgky5nke  于 2023-03-21  发布在  Java
关注(0)|答案(2)|浏览(150)

我有一个Parent-Child组件结构。在parent中,我有一个调用Web服务的函数,需要几秒钟才能完成。从parent中,我将此函数作为prop传递给child。Child组件显示按钮,并将onClick绑定到parent的函数。我如何在单击后禁用child中的按钮,直到parent的回调函数完成执行,然后重新启用它们?
例如,在下面的代码中,doOperation是这样一个函数,具有setTimeout()来模拟web服务调用延迟:

父组件:

export default function App() {
  const [val, setVal] = useState(0);
  const doOperation = (qty) => {
    setTimeout(() => {
      setVal((prev) => prev + qty);
    }, 5000);
  };

  return (
    <div className="App">
      <input placeholder={val} />
      <div style={{ display: "flex" }}>
        <ChildButtons qty={1} doOperation={doOperation} />
      </div>
    </div>
  );
}

子组件:

const ChildButtons = ({ qty, doOperation }) => {
  const [disabled, setDisabled] = useState(false);
  return (
    <div>
      <button
        onClick={() => {
          // How can I disable the button prior to call and enable after it.
          setDisabled(true);
          doOperation(qty);
          setDisabled(false);
        }}
        disabled={disabled}
      >
        {" "}
        + {qty}
      </button>
      <button
        onClick={() => {
          // How can I disable the button prior to call and enable after it.
          setDisabled(true);
          doOperation(-1 * qty);
          setDisabled(false);
        }}
        disabled={disabled}
      >
        {" "}
        - {qty}
      </button>
    </div>
  );
};

https://codesandbox.io/s/quizzical-easley-7oyeyk

更新:

虽然上面的示例代码只有两对按钮,但可能有任何数量的按钮。我有一个可滚动列表,每个列表项有两个按钮,所以从技术上讲,显示屏上可以有任何数量的按钮。

xzlaal3s

xzlaal3s1#

您必须将状态提升到父组件:

export default function App() {
  const [val, setVal] = useState(0);
  const [loading, setLoading] = useState(false);
  const doOperation = (qty) => {
    setLoading(true);
    setTimeout(() => {
      setVal((prev) => prev + qty);
      setLoading(false);
    }, 5000);
  };

  return (
    <div className="App">
      <input placeholder={val} />
      <div style={{ display: "flex" }}>
        <ChildButtons loading={loading} qty={1} doOperation={doOperation} />
      </div>
    </div>
  );
}

并在孩子体内使用:

const ChildButtons = ({ qty, doOperation, loading }) => {
  return (
    <div>
      <button
        onClick={() => doOperation(qty)}
        disabled={loading}
      >
        {" "}
        + {qty}
      </button>
      <button
        onClick={() => doOperation(-1 * qty)}
        disabled={loading}>
        {" "}
        - {qty}
      </button>
    </div>
  );
};
7fyelxc5

7fyelxc52#

你必须“promisify”回调函数,这样你就可以await它(至少这是最简单的方法-你也可以用.then来做到这一点,或者更详细地说,通过将回调传递给它将在主函数之前和之后执行的父函数):
父组件:

const sleep = (delay) => new Promise(resolve => setTimeout(resolve, delay));

export default function App() {
  const [val, setVal] = useState(0);
  const doOperation = async (qty) => {
    await delay(5000);
    setVal((prev) => prev + qty);
  };

  return (
    <div className="App">
      <input placeholder={val} />
      <div style={{ display: "flex" }}>
        <ChildButtons qty={1} doOperation={doOperation} />
      </div>
    </div>
  );
}

(so父组件实际上并没有真正改变,我只是将setTimeout调用转换为promise,以便在子组件中更容易“等待它们”)。
子组件:

const ChildButtons = ({ qty, doOperation }) => {
  const [disabled, setDisabled] = useState(false);
  return (
    <div>
      <button
        onClick={async () => {
          setDisabled(true);
          await doOperation(qty);
          setDisabled(false);
        }}
        disabled={disabled}
      >
        {" "}
        + {qty}
      </button>
      <button onClick={() => doOperation(-1 * qty)}>
        {" "}
        - {qty}
      </button>
    </div>
  );
};

(It不清楚第二个子按钮在这里应该做什么。你可以做和第一个按钮一样的事情,尽管在原始代码中你似乎没有尝试这样做,所以我在这里保留了它。看看你对另一个答案的响应,如果你想让第二个按钮独立于第一个按钮被禁用,你当然需要在子组件中有两个单独的状态变量,一个用于每个按钮的disabled状态)。

相关问题