skip to Main Content

IM GONNA LOSE MY MIND, ITS BEEN 5 HOURS, PLEASE TELL ME WHAT IS GOING ON.

in my parent I have

const [showColorbar, setShowColorbar] = useState(
  colorbarService.isColorbarToggled(viewportId),
);

I pass this value to

<SwitchButton
  label="Display Color bar"
  checked={showColorbar}
  onChange={() => {
    onSetColorbar({
      viewportId,
      options: {
        colormaps,
        ticks: {
          position: colorbarTickPosition,
        },
        width: colorbarWidth,
        position: colorbarContainerPosition,
        activeColormapName: colorbarInitialColormap,
      },
    });
  }}
/>;

and this is switchbutton

import React, { useCallback, useState } from "react";

import "./switchButton.css";

export enum SwitchLabelLocation {
  left,
  right,
}

export type SwitchButtonProps = {
  checked?: boolean;
  label?: string;
  labelLocation?: SwitchLabelLocation;
  onChange?: (checked: boolean) => void;
};

const SwitchButton = ({
  label,
  checked = false,
  onChange,
  labelLocation = SwitchLabelLocation.left,
}: SwitchButtonProps) => {
  const [isInputChecked, setIsInputChecked] = useState(checked);

  const onHandleChange = useCallback(
    (event) => {
      setIsInputChecked(event.target.checked);
      onChange?.(event.target.checked);
    },
    [onChange],
  );

  // Thanks goes to https://codepen.io/lhermann/pen/EBGZRZ for the inspiration to the code below.
  return (
    <label className="switch-button flex w-full cursor-pointer items-center justify-between text-[14px]">
      {label && labelLocation === SwitchLabelLocation.left && (
        <div>{label}</div>
      )}
      <div className="relative">
        <input
          className="absolute hidden"
          type="checkbox"
          onChange={onHandleChange}
          checked={isInputChecked}
        />
        <div className="switch-button-outer border-common-bright bg-primary-dark block h-[16px] w-[30px] rounded-full border"></div>
        <div className="switch-button-dot bg-common-bright absolute left-[4px] top-[3px] h-[10px] w-[10px] rounded-full transition duration-150 ease-in-out"></div>
      </div>
      {label && labelLocation === SwitchLabelLocation.right && (
        <div>{label}</div>
      )}
    </label>
  );
};

export default SwitchButton;

For some reason, SwitchButton does not CARE what the value of checked when it updates, lets say I toggle the switch on, and close the menu, the next time I opened the menu, even tho the checked prop being passed is true, it somehow says false, i dont get this.

I tried debugging for hours, Im expecting the child to update based on the parent, if the value for checked in the parent is true, when I opened and close the menu, the toggle should be on, not off.

2

Answers


  1. It looks like the SwitchButton only uses the ‘checked’ prop to initialize ‘isInputChecked’ and then operates on ‘isInputChecked’ only. After initializaton, ‘isInputChecked’ is never updated to stay in sync with ‘checked’.

    Fix it with useEffect within SwitchButton:

    useEffect(()=> setIsInputChecked(checked), [checked]);

    Or make SwitchButton into a controlled component by operating on ‘checked’ instead of ‘isInputChecked’ in the first place (which would be my preferred solution)

    Login or Signup to reply.
  2. It is a bad idea to initialize your state with a prop.

    The Problem Where the props are used to set the initial state of the Component is a general anti-pattern of React. This implies that the state of the component is tied to the props of the component. The issue with doing this is that the constructor is only ever called once in the life cycle of the component.

    Check out this.

    as for the solution, you can have this inside your switch. To keep your synch

    useEffect(()=> setIsInputChecked(checked), [checked]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search