0

I'm handle a toggle button status which is inside a table.. when request to api fail i need to return an error and the checked status can't change.

Checked is controlled by a specific data from api.. What is happening is, when setErrors the component is re render which I believe is the cause of toggle button changing.

 async function handleStatus(groups, status, id) {
    const response = await chengeStatus(groups, status, id);
    const { status: state, data, message } = response;

    if (state) {
      setGroups(data);
      return true;
    } else {
      setErrors({
        status: true,
        message: message,
      });
      return false;
    }
  }

Toggle Button :

const columns = [
    {
      cell: (row) => {
        return (
          <Switch
            checked={row.enable}
            handleChange={() => handleStatus(groups, row.enable, row.id)}
          ></Switch>
        );
      },
      width: "80px",
    },
  ];

Error display:

 return(
     {errors.status &&
      <Alert
      message="Erro"
      description={errors.message}
      type="error"
      showIcon
      />
      } 
    )
Giulia Lage
  • 485
  • 1
  • 5
  • 17

2 Answers2

2

It is not a good idea to solving this by preventing re-render.

What you need to do should change checked={row.enable} to defaultChecked={row.enable}

pass the default value to and create another hook for the real checked state:

const Switch = ({ defaultChecked, handleChange }) => {
  const [checked, setChecked] = useState(defaultChecked);

  useEffect(() => {

    setChecked(defaultChecked);

  }, [defaultChecked]); // Update the state if props change again.

  const onToggle = () => {
    handleChange(!checked);
    setChecked(!checked);
  }

  return <Comp checked={checked} onToggle={onToggle} />; // TODO show you the idea only
}

so checked value will remain unchanged after clicked until you refetch row data.

Allen Wong
  • 1,162
  • 1
  • 10
  • 15
  • `handleChange(!checked);` so you can use the new state as your api data by `handleChange={(isChecked) => handleStatus(groups, isChecked, row.id)}` since row.enable will not update until you refetch it, it is not that reliable if your user toggle the switch repeatedly in a short time – Allen Wong Jul 14 '20 at 18:28
  • I kinda did what you said, but a little bit different... – Giulia Lage Jul 14 '20 at 18:51
  • Frist added name prop to switch component and change checked to defaultchecked as you said... so when the request fail : const obj = {...checked} obj[id] = status setChecked(obj) – Giulia Lage Jul 14 '20 at 19:04
  • 1
    oic, so you can make onToggle as async func, and await for response result to final setChecked. `const onToggle = async () => { const result = await handleChange(!checked); setChecked(result); }` – Allen Wong Jul 14 '20 at 19:24
0

I want to share the resolution, as @AllenWong said, prevent re render, was not the best path to take to this case.

I was a little confused about the role of defaultValue and checked..

So, as Allen said I changed checked={row.enable} to defaultValue={row.enable} and create checked hook.

But I had to control checked state a little bit different, cause I have multiple checked state to control.

So, I added name prop to switch component

<Switch
   name={row.id}  
   checked={checked[row.id]}
   defaultChecked={row.enable}
   handleChange={() => handleStatus(groups, row.enable, row.id)}>
 </Switch>

And when the request fail :

const obj = {...checked} 
  obj[id] = status
      setChecked(obj)
Giulia Lage
  • 485
  • 1
  • 5
  • 17