jquense / react-big-calendar

gcal/outlook like calendar component
http://jquense.github.io/react-big-calendar/examples/index.html
MIT License
7.9k stars 2.24k forks source link

DragAndDrop Calendar doesn't update resourceHeader properly #1731

Open Bielik20 opened 4 years ago

Bielik20 commented 4 years ago

Do you want to request a feature or report a bug?

bug

What's the current behavior?

resourceHeader doesn't update properly in DragAndDrop Calendar. I have extracted simplified snipped from my code:

function useResourceToggle<TResource>(
  resources: TResource[],
): [TResource[], (resource: TResource) => void] {
  const [selected, setSelected] = useState<TResource | null>(null);
  const selector = (resource: TResource) => {
    setSelected(selected ? null : resource);
  };

  return [selected ? [selected] : resources, selector];
}

export const Component = ({ assignments, users }: Props) => {
  const [resources, resourceToggle] = useResourceToggle(users);

  return (
    <BigCalendarDnD<Assignment, User>
      events={assignments}
      resources={resources}
      components={{
        resourceHeader: (props: any) => {
          return (
            <div onClick={() => resourceToggle(props.resource)}>{props.resource.displayName}</div>
          );
        },
      }}
    />
  );
};

resourceToggle function inside resourceHeader doesn't update. This results in inability to reset selected. In other words selected will always by null when resourceToggle is called from within resourceHeader.

This happens only with DragAndDrop plugin. Without it everything works fine.

What's the expected behavior?

resourceHeader should be updated properly.

cutterbl commented 4 years ago

Do you have a CodeSandbox with what you're trying to do here? I'm trying to visualize it, but just not seeing it.

Bielik20 commented 4 years ago

I don't have CodeSandbox at hand but I can show it on pictures.

All resources:

image

Only one toggled resource:

image

cutterbl commented 4 years ago

So, when selecting a resource (header) you want it to only show that resource's items, right? Clicking again should show the other resources. Does that sound right? Just trying to fully understand your use case.

Bielik20 commented 4 years ago

Yes, that is exactly the use case. I did it with very dirty workaround and I hope to remove it. It is clearly something with DragAndDrop, because it works fine without it.

cutterbl commented 4 years ago

I've written a few different component overrides, but never messed with the resource header before. As far as I can tell though, reading through the code I'm not seeing where the hoc does anything specific with that component.

I also use the DnD hoc, and will be doing some stuff with resources in a future iteration of my work. Can't give you any clues now, but if I find something I'll post back (but, unfortunately, it won't be any time soon)

Bielik20 commented 4 years ago

Thank you, in the mean time I will post my workaround for anyone who stumbles opon this issue:

export function useResourceToggle<TResource>(
  resources: TResource[],
): [TResource[], (resource: TResource) => void] {
  const [selected, setSelected] = useState<TResource | null>(null);
  const selector = (resource: TResource) => {
    setSelected(selected ? null : resource);
  };

  return [
    (selected ? [selected] : resources).map((res) => {
      // @ts-ignore
      res.__toggle = () => selector(res);
      return res;
    }),
    selector,
  ];
}

It is dirty and ugly but it works. You must use __toggle method on a resource to toggle, not the returned selector callback.