Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mechanism to retrieve previous index/step #419

Open
springroll12 opened this issue Jul 25, 2019 · 7 comments
Open

Mechanism to retrieve previous index/step #419

springroll12 opened this issue Jul 25, 2019 · 7 comments

Comments

@springroll12
Copy link

Is there a way to determine which slide was last visited at render time?

It would be very helpful to the following use cases:

  1. Slide transitions: If you need to establish the "direction" you are moving in the slide deck to provide different animations (e.g. slide in/out animations similar to previous versions of mdx-deck)

  2. Customizing Appear: Determine what should happen on a step within an Appear. Similar to Advanced step control #193.

Maybe this can be accomplished through reach router history but I haven't found a way to do so yet.

@jxnblk
Copy link
Owner

jxnblk commented Jul 25, 2019

I think you could use the useDeck hook to keep track of that somewhere, but not entirely sure I understand what you’re trying to do

https://github.com/jxnblk/mdx-deck/blob/master/docs/api.md#usedeck

@springroll12
Copy link
Author

I'm not sure that useDeck is enough, but maybe I'm missing something.

For the first use case, I'm trying to recreate the slide transitions from earlier versions of mdx-deck default theme. (An example here: https://simply-react.netlify.com/). It is a basic slide-in and slide-out animation.

If you are moving forwards in the slide deck, you want slides to "exit" to the left and "enter" from the right. However if you're going backwards through the deck, you want the opposite. I think this is difficult to achieve without being able to establish the direction the user is headed in the slide deck. Unless I'm missing something, the only ways to do this are:

  1. Somehow compare urls of previous slides through the history api, or

  2. Compare the current slide index to the previous slide index (or step)

As far as I know useDeck does not expose the previous slide index, only the current slide.

@jxnblk
Copy link
Owner

jxnblk commented Jul 25, 2019

Yeah, the “direction” is something you’d need to keep track of but useDeck will give you the current slide and step

@springroll12
Copy link
Author

Still struggling with this.

In order to achieve directional transitions, I need to store the previous slide index in some sort of global state store. But so far I haven't figured out where to store a state variable that would be shared with all slides?

Since each slide needs to be wrapped in an individual layout component, you should not store state in these layouts, because even if two slides have the same layout, they will not share state since they are different instances of the same component.

Is the Provider the right place to store this? If so, how might I pass it down to a layout component? It doesn't appear that props are passed from Provider->Layout directly, and from my reading of Slide.js each slide has its own context provider.

Maybe a better idea would be for me to create a new useDeckEnhanced hook that contains the context I need?

Any guidance you could provide would be greatly appreciated.

@jxnblk
Copy link
Owner

jxnblk commented Aug 21, 2019

Right, so to store the state that you'd need for this I think you'd want the following:

  • Custom Provider
  • useState or useReducer hook in the Provider
  • A custom context provider that includes this state + any other context you'd want to use in your slides (context allows you to pass data down without manually passing props)
  • A custom hook for your stateful context that you can use in your slides

@springroll12
Copy link
Author

Thank you for pointing me in the right direction!

I think I'm almost there, but I think I'm struggling to understand how useState/useEffect should be used in the custom provider. It seems that the provider renders a few extra times with the default state values at the end of a slide transition (e.g. 1->2).

Here is my custom provider:

import React, { useState, useLayoutEffect, useEffect } from 'react'
import { useDeck } from 'mdx-deck'
import ExtraContext from '../hooks/ExtraContext'

export default props => {
    const state = useDeck();
    const [ prevIndex, setPrevIndex ] = useState(state.index);
    const [ direction, setDirection] = useState(10); // Set this to 10 to make it easier to distinguish the initial value
    useLayoutEffect(() => {
        let dir = 1;
        console.log("state.index: " + state.index);
        console.log("prevIndex: " + prevIndex);
        if (state.index !== prevIndex) {
            if (prevIndex > state.index) {
                dir = -1;
            }
            if (dir !== direction) {
                console.log("useEffect setting direction: " + dir);
                setDirection(dir);
            }
        }
    }, [state.index]);

    useEffect(() => {
        return () => {
            console.log("Setting prevIndex: " + state.index);
            setPrevIndex(state.index);
        }
    }, []);
    console.log("FINAL DIR: " + direction); // Prints the "correct" value, then the inital value on a subsequent render??
    console.log("======================\n");
    return (
        <div>
            <ExtraContext.Provider value={{direction: direction}}>
                {props.children}
            </ExtraContext.Provider>
        </div>
    )
}

Note that I am using a custom layout as well which reads the context value with ExtraContext.Consumer. No state changes are made beyond the custom provider, so hopefully reading the context doesn't impact how the provider renders.

Some console output for one slide transition with this custom provider. Note that it renders several times (beyond what seems to be needed by the state updates done in useEffect/useLayoutEffect) and finishes with the initial value for the direction state.

FINAL DIR: 10
provider.js:1 ======================
 
provider.js:1 state.index: 1
provider.js:1 prevIndex: 0
provider.js:1 useEffect setting direction: 1
provider.js:1 FINAL DIR: 1
provider.js:1 ======================
 
provider.js:1 FINAL DIR: 1
provider.js:1 ======================
 
provider.js:1 FINAL DIR: 10
provider.js:1 ======================
 
provider.js:1 Setting prevIndex: 0
provider.js:1 state.index: 1
provider.js:1 prevIndex: 1
provider.js:1 FINAL DIR: 10
provider.js:1 ======================
 

I have a feeling that there is something about the useState/useEffect combination that I have not figured out yet, but I thought I would ask here to ensure the problem I'm seeing is not related to how the Provider renders in mdx-deck.

@springroll12
Copy link
Author

springroll12 commented Aug 22, 2019

Another quick test with a basic custom provider and basic layout seems to indicate that the provider renders multiple (3) times per slide transition:

Provider.js

import React from 'react'

export default props => {
    console.log("render provider");
    return (
        <div>
            {props.children}
        </div>
    )
}

This shouldn't be an issue since useState should use the state values from previous renders, but it appears it does not? Very strange.

I also tested with a basic useState/useEffect to see if the state changes are picked up on subsequent renders, but it seems they are not:

Provider.js

import React, { useState, useEffect } from 'react'

export default props => {
    console.log("render provider");
    let [ test, setTest ] = useState(true);
    useEffect(() => {
        setTest(false);
    }, []);
    console.log("test: " + test);
    return (
        <div>
            {props.children}
        </div>
    )
}

Console output:

render provider
test: false
render provider
test: true
render provider       << Why does this last render occur?
test: false

Maybe the Provider is not the right place to put state changes?

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

No branches or pull requests

2 participants