React-joyride: [V2]: Reset tour once the tour has completed

Created on 8 May 2018  路  12Comments  路  Source: gilbarbara/react-joyride

Goal: In V2, once a round of the tour has completed, reset the tour (i.e. {stepIndex: 0, run: false}) so that the user can trigger the tour again from an external button.

My first attempt was to reset the state of the stepIndex and run at the tour:end, but the completed joyride component had already been mounted so setting the local state did nothing for it.

My workaround was to check the next index and the size of the steps array of the step:after event. Comparing the two would decide whether to reset the local state of the stepIndex and run or to advance to next step of the tour. This works because it catches the stepIndex before it trigger a tour:end event type.

callback = (tour) => {
    const { action, index, type } = tour;

    if (type === EVENTS.TOUR_END) {
      // NOTE: This step doesn't work.  Tour ends and doesn't reset.
      this.setState({
        stepIndex: 0,
        run: false
      });
    } else if (action === ACTIONS.CLOSE) {
      this.setState({
        stepIndex: index
      });
    } 
    else if ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND].includes(type)) {
      // HACK: Comparing the next index to the size of the steps array
      let nextIndex = index + (action === ACTIONS.PREV ? -1 : 1);

      if (nextIndex === size) {
        this.setState({
          stepIndex: 0,
          run: false
        });
      } else {
        this.setState({ stepIndex: nextIndex });
      }
    }
  };

Question: My method above seems a little hacky. Is there an easier way to reset the tour without having to compare the next index to the size of the steps array?

UPDATE: Added the step in which I maintain the index when ACTIONS:CLOSE occurs.

Most helpful comment

@jnguyen10 to reset the tutorial entirely, I just increment the key property on the main component div, which forces React to 'reset' the component. So on 'reset event you want', set the key on the state to increment + 1, and then set the main div container to pull in the key property from the state.

So, <div key={this.state.key}>joyride stuff here</div>, will hackily force the tutorial to reset since the component itself resets.

Not sure if this will have other side effects for you, but this worked well for me

All 12 comments

Do you really need to set the stepIndex? Why?

I'm using stepIndex because I wanted to control the step when ACTIONS.CLOSE occurs. I wanted to maintain the index instead of advancing it to the next beacon/step.

@gilbarbara. There are strange behavior in react-joyride V2. When I click on the cross button at the step, I expect the tour to stop, but this does not happen, instead the next step is turned on. Maybe there is some explanation for this? I have seen it in your demo

@Chypa74 This has nothing to do with the current issue. Please open a new issue with detailed information, like which demo are you talking about and if it's a continuous tour and/or controlled.

I have created new issue #357

@jnguyen10 to reset the tutorial entirely, I just increment the key property on the main component div, which forces React to 'reset' the component. So on 'reset event you want', set the key on the state to increment + 1, and then set the main div container to pull in the key property from the state.

So, <div key={this.state.key}>joyride stuff here</div>, will hackily force the tutorial to reset since the component itself resets.

Not sure if this will have other side effects for you, but this worked well for me

@ragnar-pwninskjold It's awesome. Thank you for advice. It's work
Also work like this <Joyride key={name} {...otherJoyrideProps}/>

@ragnar-pwninskjold I noticed with this forced component reset, the steps would increase because the child components that have the addStepsToWalkthrough function would rerun. This would continue to increment that number of steps each time you rerun the walkthrough.

@jnguyen10 I don't think so, no. If you force the parent component to unmount and reset itself, would that logic not flow down to all of its children as well? If you reset the Joyride component, all children of the component would also reset. This solution has worked for me, so no unintended incrementing still happening. Also not sure what the addStepsToWalkthrough function is, haven't used that, or if I have its hidden behind a layer I haven't had the need to interact with

@ragnar-pwninskjold ahh, I understand now. I had added the key to the main div OUTSIDE of the Joyride component instead of in the Joyride component (or Joyride's child components). The addStepsToWalkthrough allows you to add the walkthrough steps programmatically from the Main Component's child components.

Thanks for the suggestion!

Callback

callback(tour) {
    const { action, index, size, type } = tour;

    if (type === EVENTS.TOUR_END) {
      this.setState({
        mainKey: this.state.mainKey + 1,
        stepIndex: 0,
        walkthrough: false
      });
    } else if ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND].includes(type)) {
      let nextIndex = index + (action === ACTIONS.PREV ? -1 : 1);

      this.setState({ stepIndex: nextIndex });
    };
}

DIDN'T WORK

<div key={ this.state.mainKey }> // <<<<<<<<<<<< NO
  <Joyride
    ref={c => (this.joyride = c)}
    callback={this.callback}
    continuous={walkthrough}
    run={walkthrough}
    showProgress
    showSkipButton
    spotlightClicks
    spotlightPadding={5}
    steps={steps}
    stepIndex={stepIndex}
  />
  <div>
    <HeaderSection addStepsToWalkthough={this.addStepsToWalkthough} />
    <MainSection addStepsToWalkthough={this.addStepsToWalkthough} />
  </div>
</div>

DID WORK

<div>
  <Joyride
    ref={c => (this.joyride = c)}
    key={ this.state.mainKey } // <<<<<<<<<<<< YES
    callback={this.callback}
    continuous={walkthrough}
    run={walkthrough}
    showProgress
    showSkipButton
    spotlightClicks
    spotlightPadding={5}
    steps={steps}
    stepIndex={stepIndex}
  />
  <div>
    <HeaderSection addStepsToWalkthough={this.addStepsToWalkthough} />
    <MainSection addStepsToWalkthough={this.addStepsToWalkthough} />
  </div>
</div>

@gilbarbara Having to change the key in order to reset the tour state works but is not very pretty and needs to make a small hack.

Could it be a resetOnTourEnd prop that reset the tour state when tour:end occure ?

I've fixed the behavior in 2.0.0-13.
You'll need to be in controlled mode and using the stepIndex prop.

Check the Controlled example in https://codesandbox.io/s/2zpjporp4p

Was this page helpful?
0 / 5 - 0 ratings

Related issues

captainkovalsky picture captainkovalsky  路  6Comments

rlajous picture rlajous  路  3Comments

leantide picture leantide  路  5Comments

jjordy picture jjordy  路  3Comments

SamSunani picture SamSunani  路  4Comments