0

I have a requirement where i have couple of components out of which only one could be displayed at a particular point of time. The components are of varying heights.

When a user clicks on the button in the currently displayed component i need to

  1. Show a loader to the user
  2. Render the component in background.
  3. Wait till the rendered component calls onLoad callback.
  4. Once onLoad callback is recived, hide the loader.

Something like below (Code sandbox here)

const [loading, setLoading] = useState(false);
  const [activeElement, setActiveElement] = useState("Component1");

  const onLoad = () => {
    setLoading(false);
  };

  const onClick = () => {
    setLoading(true);
    setActiveElement(
      activeElement === "Component1" ? "Component2" : "Component1"
    );
  };

  return (
    <div className="container">
      <ViewWrapperHOC loading={loading}>
        {activeElement === "Component1" ? (
          <Component1 onLoad={onLoad} onClick={onClick} />
        ) : (
          <Component2 onLoad={onLoad} onClick={onClick} />
        )}
      </ViewWrapperHOC>
    </div>
  );

I was planning to write a wrapper component (ViewWrapperHOC in above example) to show the transition between the components and show the loader. Now the problem is when i try to animate, since i have to render both the progressbar and children in viewwrapper, the animation seems to be very glitchy.

Code sandbox here

Could someone suggest a way to fix this. I am open to any alternate animations or any alternate approach to achieve this in any form of pleasant ux. Thanks in advance.

Vikhyath Maiya
  • 3,122
  • 3
  • 34
  • 68

1 Answers1

1

In that second code sandbox, your issue is that as soon as you click, you are changing which element is active, therefore it gets rendered.

You could put a small timeout in the onClick function:

  const onClick = () => {
    setLoading(true);

    setTimeout(() => {
      setActiveElement(
        activeElement === "Component1" ? "Component2" : "Component1"
      );
    }, 100);
  };

Then in view wrapper you'll need to get rid of transition: "200ms all ease-in-out, 50ms transform ease-in-out".

You need to have a known height here to be able to make a smooth change in height transition.

See codesandbox fork

Version 2:

Here is version 2, using refs. Main changes are moving the box shadow out of the components to the container in view wrapper. Adding refs to the components, passing the active ref to the wrapper and then setting the height with height transition.

To be honest, I'd probably restructure the code a lot if I had the time.

Steve
  • 4,372
  • 26
  • 37
  • That is the point .. as i mentioned in the question, it was done internationally. I need to show the loader, untill the next component is mounted and calls onLoad callback. – Vikhyath Maiya Jan 25 '22 at 19:58
  • So with this solution, I am seeing the green component, then I click the button, it goes away and I see the skeleton loader for a while until I then see the red component. Is this correct? – Steve Jan 25 '22 at 20:03
  • Are you asking about your solution ? Since loading state gets set only when the new component is loaded, it will keep on infinitely show the skeleton loader – Vikhyath Maiya Jan 25 '22 at 20:13
  • If you are asking what is the expectation. Then . Component 1 shows -> User clicks on button -> Skeleton loader shown -> Component 2 mounts in background -> Component 2 callbacks onLoad -> Loader goes away. – Vikhyath Maiya Jan 25 '22 at 20:14
  • Yeah got you. Fully missed the mark there sorry. Check updated answer. I might do a less hacky solution using refs so you can get the transition height change too. – Steve Jan 25 '22 at 20:47
  • @VikhyathMaiya See version two in there aswell. Not perfect but might set you on the right path. – Steve Jan 25 '22 at 21:15
  • 1
    Thanks for guiding me in the right direction. I will check on it and try to improvise. This helps. Thank you :) – Vikhyath Maiya Jan 26 '22 at 11:54