americanexpress / react-albus

✨ React component library for building declarative multi-step flows.
Apache License 2.0
1.1k stars 89 forks source link

Avoid double-rendering on mount #27

Closed pfeiffer closed 6 years ago

pfeiffer commented 6 years ago

At the moment there are several render passes as the steps are initialized, mounted, and set on the wizard-object. When using step.id as key for animations - as shown in the examples - this causes the first step to be animated when being mounted (as the step.id changes from undefined to the ID of the first step.

The default behaviour of CSSTransition is to not animate on component mount, but since the step.id changes during mount, it will be animated.

It can be seen in the example where the 'Gandalf' slide is fading in even on first render.

Is there a way to avoid this?

jackjocross commented 6 years ago

The <Wizard /> component renders multiple times on mount however each <Step /> component only renders once on mount, here is a codesandbox that logs out the renders.

In the animation example we are using <TransitionGroup /> which animates components as they mount, in this case each <Step />.

If you want to avoid animating a step the first time it is viewed, I would probably store some state like wizardViewed in the component that is rendering the <Wizard /> and toggle animations in each step based on that.

pfeiffer commented 6 years ago

I did some testing and solved it by adding a key on the <TransitionGroup> that's dependent on whether the steps are initialised or not. This solves the initial animation for the first step:


<Wizard render={({ step }) => (
    <TransitionGroup key={step.id && 'initialized'}>
        <CSSTransition key={step.id}>
            <Steps>
                <Step ...>
            </Steps>
        </CSSTransition>
    </TransitionGroup>
)}>
jackjocross commented 6 years ago

Ah sorry I think I misunderstood your original question, that solution makes sense 👍.

I wonder if we want an as prop on <Steps> for this use case.

<Wizard>
  <Steps as={TransitionGroup}>
    <CSSTransitionStep />
  </Steps>
</Wizard>

Then you could only render TransitionGroup once the steps are initialized.