Skip to content
This repository has been archived by the owner on May 17, 2024. It is now read-only.

New instance of the tour created on every render of the provider (Relates to #282 ?) #587

Closed
YM-KA opened this issue Jul 9, 2021 · 4 comments

Comments

@YM-KA
Copy link

YM-KA commented Jul 9, 2021

Every time a component, that uses the tour context, is re-rendered a new instance of the tour is created.
The new instance is then provided to child components via the context.

If you're trying to trigger the tour automatically using an effect, this will result in the tour being re-created (the tour id will change) every time the provider component is re-rendered.

We are accessing the tour in several components to read the isActive() flag. Maybe this is wrong?

import { useContext } from 'react'
import { ShepherdTourContext } from 'react-shepherd'
const tour = useContext(ShepherdTourContext)

AppRouter.js - Mounting point

import { Router, Route, Switch } from 'react-router-dom'
import { steps } from '../components/tour/steps';
...
const AppRouter = ({ isTourCompleted, ...rest }) => {
  //shepherd tour
  const tourOptions = {
    defaultStepOptions: {
      classes: 'shepherd-theme-introTour',
      cancelIcon: { enabled: true }
    },
    useModalOverlay: true
  };
  
  return (
    <React.Fragment>
      <Router history={history}>
      <ShepherdTour steps={steps} tourOptions={tourOptions}>
        {(!isTourCompleted) &&
          <StartTour autoStart={!isTourCompleted}/>
        }
        <Suspense fallback={<LoadingSpinnerFullPage />}>
        <div className="appContainer">
		  <Route path="" render={() => <Sidebar />} />
		  ...
      </ShepherdTour>
      </Router>
    </React.Fragment>
  )	
}


const mapStateToProps = (state) => ({
  isTourCompleted:state.userSettings.isTourCompleted
})

export default connect(mapStateToProps)(AppRouter)

StartTour.js Component - Auto starts the tour

import { useEffect, useContext } from 'react'
import { ShepherdTourContext } from 'react-shepherd'

export const StartTour = (props) => {
  console.log('Shep StartTour props', props)
  const tour = useContext(ShepherdTourContext);
  useEffect(() => {
    if(props.autoStart &! tour.isActive()){
      setTimeout(()=>{
        tour.start()
        console.log('Shep tour', tour.id, tour.isActive())
      }, 5000)
    }
  }, [props.autoStart])
  return null
}

Sidebar.js Component - Has a onClick() to start the tour

export const Sidebar = (props) => {
  const tour = useContext(ShepherdTourContext);
  console.log('Shep sidebar tour', tour.id)
  return (
    <React.Fragment>
	  ...
      <p className="tourLinkContainer" onClick={tour.start}>
        <img className="tourLinkContainer__img" src='/images/busStop.svg' />
        <span className="tourLinkContainer__text">{isNavOpen ? "Guide" : ""}</span>
      </p>	  
    </React.Fragment>
  );
}

The console output when updating the 'isTourCompleted' flag in the store will be:

***INITIAL RENDER***
Shep sidebar tour tour--34d97abe-ea40-43a2-965e-3fd201f272cd
Shep sidebar tour tour--c1f5a161-9fa3-43e4-a109-bcbb0d8a27a2
Shep sidebar tour tour--c1f5a161-9fa3-43e4-a109-bcbb0d8a27a2
Shep sidebar tour tour--c1f5a161-9fa3-43e4-a109-bcbb0d8a27a2
***SET 'isTourCompleted' FLAG IN STORE***
Shep StartTour props Object { autoStart: true }
Shep StartTour props  Object { autoStart: true }
Shep sidebar tour tour--83666ead-d23b-4751-9159-1ef3c1ea0864
Shep sidebar tour tour--83666ead-d23b-4751-9159-1ef3c1ea0864
Shep sidebar tour tour--8a4dce0a-3f9f-40e3-91dc-e23065961e99
Shep sidebar tour tour--8a4dce0a-3f9f-40e3-91dc-e23065961e99
Shep StartTour tour tour--83666ead-d23b-4751-9159-1ef3c1ea0864 true

The StartTour component should not use the old instance of the tour.
(the id should be 'tour--8a4dce0a-3f9f-40e3-91dc-e23065961e99')

When clicking the < p > in Sidebar.js the correct id is used (tour--8a4dce0a-3f9f-40e3-91dc-e23065961e99).

How can this behaviour be prevented?

@YM-KA YM-KA changed the title New instance of the tour created on every render of the provider (#282) New instance of the tour created on every render of the provider (Relates to #282 ?) Jul 9, 2021
@chuckcarpenter
Copy link
Collaborator

@YM-KA perhaps setting up a codesandbox would make it easier to debug together? Offhand, I'd think it was correct anytime the component rerenders that you'd get a new tour, so you'd either prevent that via state in your app or perhaps only having the tour on a certain route, etc. Shepherd doesn't have any mechanism for to prevent multiple tours, that all falls on the app itself.

@busyxiang
Copy link

I am having the same issue as well using NextJS, it will become a new instance when I change the page, any solutions so far?

@busyxiang
Copy link

I am having the same issue as well using NextJS, it will become a new instance when I change the page, any solutions so far?

In the end, I just use the JS version instead and handle the instance myself

@chuckcarpenter
Copy link
Collaborator

@busyxiang could you provide an example with this behavior? Seems like it's been fixed since, #283.

Otherwise, care to share how you proceeded and see if there's anything we could change/improve?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants