How to handle loading/disabled states and click functions of multiple buttons using the same reusable <Button/> component across a screen?

huangapple go评论45阅读模式
英文:

How to handle loading/disabled states and click functions of multiple buttons using the same reusable <Button/> component across a screen?

问题

The problem: 我有一个父组件,其中有多个按钮。每个按钮将从不同的API端点获取数据。要求是,当单击按钮时,它应该进入加载状态,并且在调用完成之前应禁用其他按钮。

What I have tried: 我创建了两个单独的按钮组件 <Button1><Button2>,它们有各自的 isLoading 状态。我在父组件中声明了一个 disabled 状态,并将其及其设置处理程序作为 props 传递给子按钮组件。当单击 onClick 函数时,我将 isLoading 状态设置为 true,并将 setDisabled 属性设置为 true,通知父组件。我在每个子按钮组件上为 disabled 属性编写了逻辑,如 disabled={!isLoading && disabled},这意味着如果不是正在进行API调用/加载的按钮,按钮将被禁用。

Solution support required: 我需要一种方法来通过使用一个单一的 <Button> 组件来减少代码重复,而不是多次将多个按钮组件作为子组件传递给父组件,但不知道如何在其中任何一个被单击时分离它们的 isLoadingdisabledonClick 状态和处理程序,并且它应该像上面沙箱中的方法1一样工作。

英文:

The problem: I have a Parent component that has multiple buttons. Each button will fetch data from the API for different endpoints. The requirement is when a button is clicked it should go into a loading state and the other buttons should be disabled till the call completes.

What I have tried: I created two separate button components &lt;Button1&gt; and &lt;Button2&gt; which have their own isLoading state. I have declared a disabled state in the Parent component and I am passing this and its set handler down as props to the child button components. When the onClick function is hit I set the isLoading state to true and the setDisabled property to true notifying the parent component about it. I have written the logic for the disabled attribute on each child button component as diabled={!isLoading &amp;&amp; disabled} which means that the button will be disabled if it is not the one making the API call/loading.

Adding the code sandbox for this approach's implementation and the one needed: https://codesandbox.io/s/sharp-hill-r4rsk0?file=/src/App.js

Solution support required: I need a way to reduce code duplication by using one single &lt;Button&gt; component instead of multiple button components as a child in parent multiple times but don't know how to separate their isLoading, disabled, and onClick states and handlers when either of them is clicked and it should work as in the approach 1 as seen in the sandbox above.

答案1

得分: 1

React允许您通过传递不同的props来保留同一组件的多个实例。它会自动管理每个实例的不同状态。因此,您目前与不同按钮组件一起使用的相同方法也可以用于单个按钮组件。

这里是带有多个实例的单个按钮组件的修改后的codesandbox

按钮组件如下:

function Button({ label, changeDisabledState, disabled }) {
  const [isLoading, setIsLoading] = useState(false);

  const onSubmit = () => {
    changeDisabledState(true);
    setIsLoading(true);
    setTimeout(() => {
      changeDisabledState(false);
      setIsLoading(false);
    }, 3000);
  };

  return (
    <div>
      {isLoading ? (
        <div>Loading...</div>
      ) : (
        <button disabled={!isLoading && disabled} onClick={onSubmit}>
          {label}
        </button>
      )}
    </div>
  );
}

当单击时调用onSubmit函数时,它将仅将该特定按钮实例的isLoading状态更改为true。此组件的其他实例仍将具有isLoading状态为false。disabled状态由父组件维护。因此,所有按钮实例的此状态将相同。由于我们将禁用状态应用为disabled={!isLoading && disabled},因此当前点击的按钮将不会被禁用,因为在这个实例中isLoading将为true

英文:

React allows you to keep multiple instances of the same component by passing different props. It automatically manages the different states of each instance. So the same approach that you are currently using with different button components can be used with single button component.

Here the modified codesandbox having single button component with multiple instances.

The button component is as follows.

function Button({ label, changeDisabledState, disabled }) {
  const [isLoading, setIsLoading] = useState(false);

  const onSubmit = () =&gt; {
    changeDisabledState(true);
    setIsLoading(true);
    setTimeout(() =&gt; {
      changeDisabledState(false);
      setIsLoading(false);
    }, 3000);
  };

  return (
    &lt;div&gt;
      {isLoading ? (
        &lt;div&gt;Loading...&lt;/div&gt;
      ) : (
        &lt;button disabled={!isLoading &amp;&amp; disabled} onClick={onSubmit}&gt;
          {label}
        &lt;/button&gt;
      )}
    &lt;/div&gt;
  );
}

when the function onSubmit is called on click, it will change the isLoading state of only that particular button instance to true. Other instances of this component will still have isLoading states as false. The disabled state is being maintained in the parent component. So this state will be same for all the button instances. As we are applying disabled state as disabled={!isLoading &amp;&amp; disabled}, the current clicked button will not be disabled because isLoading will be true in this instance.

答案2

得分: 1

Here is the translated code:

请检查我的代码是否符合您的要求

import { useState } from "react";

const Button = ({ children, loading, disabled, onClick }) => {
  return (
    <button disabled={disabled} onClick={onClick}>
      {loading ? "加载中..." : children}
    </button>
  );
};

const ButtonWrapper = ({ url, onFetching, disabled, children }) => {
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState();

  const getData = () => {
    setLoading(true);
    onFetching(true);
    fetch(url)
      .then((response) => response.json())
      .then((json) => setData(json))
      .finally(() => {
        setLoading(false);
        onFetching(false);
      });
  };

  return (
    <Button loading={loading} disabled={disabled} onClick={() => getData()}>
      {children}
    </Button>
  );
};

export default function App() {
  const [isFetching, setIsFetching] = useState(false);

  return (
    <div className="App">
      <ButtonWrapper
        onFetching={setIsFetching}
        disabled={isFetching}
        url="https://jsonplaceholder.typicode.com/posts"
      >
        获取文章
      </ButtonWrapper>

      <ButtonWrapper
        onFetching={setIsFetching}
        disabled={isFetching}
        url="https://jsonplaceholder.typicode.com/albums"
      >
        获取相册
      </ButtonWrapper>

      <ButtonWrapper
        onFetching={setIsFetching}
        disabled={isFetching}
        url="https://jsonplaceholder.typicode.com/users"
      >
        获取用户
      </ButtonWrapper>
    </div>
  );
}

Please note that I've translated the code and removed the request for further translation. If you have any questions or need further assistance with this code, please feel free to ask.

英文:

Please check whether my code meets your requirement.

import { useState } from &quot;react&quot;;
const Button = ({ children, loading, disabled, onClick }) =&gt; {
return (
&lt;button disabled={disabled} onClick={onClick}&gt;
{loading ? &quot;Loading...&quot; : children}
&lt;/button&gt;
);
};
const ButtonWrapper = ({ url, onFetching, disabled, children }) =&gt; {
const [loading, setLoading] = useState(false);
const [data, setData] = useState();
const getData = () =&gt; {
setLoading(true);
onFetching(true);
fetch(url)
.then((response) =&gt; response.json())
.then((json) =&gt; setData(json))
.finally(() =&gt; {
setLoading(false);
onFetching(false);
});
};
return (
&lt;Button loading={loading} disabled={disabled} onClick={() =&gt; getData()}&gt;
{children}
&lt;/Button&gt;
);
};
export default function App() {
const [isFetching, setIsFetching] = useState(false);
return (
&lt;div className=&quot;App&quot;&gt;
&lt;ButtonWrapper
onFetching={setIsFetching}
disabled={isFetching}
url=&quot;https://jsonplaceholder.typicode.com/posts&quot;
&gt;
Fetch Posts
&lt;/ButtonWrapper&gt;
&lt;ButtonWrapper
onFetching={setIsFetching}
disabled={isFetching}
url=&quot;https://jsonplaceholder.typicode.com/albums&quot;
&gt;
Fetch Albums
&lt;/ButtonWrapper&gt;
&lt;ButtonWrapper
onFetching={setIsFetching}
disabled={isFetching}
url=&quot;https://jsonplaceholder.typicode.com/users&quot;
&gt;
Fetch Users
&lt;/ButtonWrapper&gt;
&lt;/div&gt;
);
}

I have coded in the same file but you can make a separate file, also make sure to change the network Network throttling to Slow 3G or something to see the functionality clearly.

demo: https://codesandbox.io/s/cranky-shannon-vtwmz8?file=/src/App.js

huangapple
  • 本文由 发表于 2023年5月13日 14:57:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76241460.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定