Jump to content
Search Community

Reverse problem in React

Tommy81 test
Moderator Tag

Recommended Posts

Hi there everybody,

 

At this git repository  https://Tommy81@bitbucket.org/Tommy81/react-gsap-reverse.git I've isolated the code coming from a real React project I'm working on.
I'm not understanding why the .modal-wrapper DOM element in apparently not correctly animated by GSAP Timeline reverse method.
When you click on the "Open Modal" button, the modal DOM elements (.modal, .modal-overlayer and .moda-wrapper) are correctly animated.
When you click the modal close button (.modal-close) .modal-wrapper it seems to me the .modal-wrapper element in not correctly animated because when you click again on "Open Modal" button from second time on, the modal is now showing again...

I suspect the problem should be related to the way I set the initial Timeline and css settings...

Down here I give a restricted code preview of the git I shared:

INITIAL CSS

  html, body {
    box-sizing: border-box;
    height: 100%;
    width: 100%;
  }

  body {
    background: white;
    color: black;
    padding: 50px;
  }

.modal {
  height: 0;
  left: 0;
  overflow: hidden;
  position: fixed;
  top: 0;
  width: 100%;
}

.modal-overlayer {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%,-50%);
  height: 100%;
  width: 100%;
  background: rgba(0,0,0,0.5);
  opacity: 0;
  z-index: 0;
}

.modal-wrapper {
  position: absolute;
  background: white;
  border-radius: 5px;
  left: 50%;
  top: 50%;
  transform: translateX(-50%) translateY(-50%);
  max-height: 80%;
  opacity: 1;
  width: 80%;
  z-index: 1;
}

REACT & GSAP ESSENTIAL CODE

 

const Modal = props => {

  const body = document.querySelector('body');
  const container = document.querySelector('.modal');
  const overlayer = document.querySelector('.modal-overlayer');
  const wrapper = document.querySelector('.modal-wrapper');

  const tl = gsap.timeline({
    defaults: {
      ease: "expo.out",
    },
    onReverseComplete: () => {
      unlock();
      props.openModal(false);
    },
  });

  tl.to(overlayer,{
    duration: 0.5,
    opacity: 1,
  });
  tl.from(wrapper,{
    duration: 0.8,
    opacity: 0,
    yPercent: '-=30',
  });

  const lock = () => {
    body.style = `
      overflow: hidden;
      position: relative;
    `;
    container.style = `
      height: 100%;
    `;
  }
  const unlock = () => {
    body.style = `
      overflow: unset;
      position: unset;
    `;
    container.style = `
      height: 0;
    `;
  }
  const modal = status => {
    if (status === 'open') {
      lock();
      tl.play();
    }
    if (status === 'close') {
      tl.reverse();
    }
  }

  useEffect(() => {
    if (props.modal) modal('open');
  });

  return(/* render stuff here */)

}

Thank you very who's gonna help me... ^^

See the Pen by Tommy81 (@Tommy81) on CodePen

Link to comment
Share on other sites

Hi and welcome to the GreenSock forums.

 

Yeah animations using hooks can get a little bit tricky compared with regular class components.

 

I couldn't reach the page of your repo, so I can only work using the code you posted. Based on that you're missing a way to update the modal's prop back to falsy. Right now you open the modal by using this:

if (props.modal) modal('open');

Then you close the modal by using this method:
 

const modal = status => {
  if (status === 'open') {
    lock();
    tl.play();
  }
  if (status === 'close') {
    tl.reverse();
  }
}

But props.modal is still truthy and I'm guessing that the button you have to show the modal updates that prop to true, but since the prop is now never updated you can't see the modal again.

 

My advice would be to instead of using that modal method to reverse the timeline inside the modal component, pass a method that actually changes the props.modal back to false and toggle the modal tween using that in the useEffect() call.

 

Here is a modal sample that works on class components:

https://stackblitz.com/edit/gsap-react-state-modal?file=index.js

 

As you can see in the modal.js file there are two key parts, first the state property modalVisible, which is passed as a prop to the modal component, and the setModalVisible method, which updates that state property:

this.setModalVisible = this.setModalVisible.bind(this);

// inside the render method
<ModalComponent
  visible={this.state.modalVisible}
  close={this.setModalVisible.bind(null, false)}
/>

Then inside the modalComponent.js file you basically have this inside the componentDidUpdate() method:

this.modalTween.reversed(!this.props.visible);

Which basically toggles the timeline instance.

 

First pass a method to the modal (as a prop) in order to close it by updating the props.modal property:

// In the parent component or perhaps where you're using the portal to create the modal
<MyModal
  modal={showModal}
  close={closeModal}
>

 

You should create the GSAP instance in the initial render with:

useEffect(() => {
  // create GSAP instance here
}, [])

And then check for changes in the props.modal property:

useEffect(()=>{
  if(props.modal) {
    tl.play();
  } else {
    tl.reverse();
  }
}, [props.modal]);
// In your parent component or where you're adding the modal using portal
const closeModal = () => {
  setModalVisible(false);
}

return(
  <MyModal
    modal={modalVisible}
    close={closeModal}
  >
)

Finally you can create a reduced live sample in either codesandbox or stackblitz, which makes it easier and simpler to tackle issues.

 

Happy tweening!!!

  • Like 4
Link to comment
Share on other sites

Hi Rodrigo,

 

first of alla thanks a lot for answering! :-)

It's very strange you can't reach the repo, I tried just before responding, assuring me to be not logged-in in my Bitbucket account and the link worked correctly...
The fact you haven't see the full code code has caused some feedbacks which need more clarifications from me.
Lets do it.

Here is the essential App component code:

// Main component
const App = props => {

  const [modal,openModal] = useState(false);

  return (
    <div className="app">
      <CommonStyles />
      <Button
        display="block"
        label="Open modal"
        onClick={()=> openModal(true)}
        width="100%"
      />
      <Modal
        modal={modal}
        openModal={val => openModal(val)}
      />
    </div>
  );
}

// Exports
export default App;

As you can see Modal component has a openModal prop which is used just the toggle the modal flag in the App component state from which in turn depends te modal prop of Modal component.
modal flag and openModal hook function are created with the useState hook.

modal flag is false at start, changed to true clicking the "Open modal" button and then set back to false in the Timeline.onReverseComplete() callback:

  const tl = gsap.timeline({
    defaults: {
      ease: "expo.out",
    },
    onReverseComplete: () => {
      unlock();
      props.openModal(false);
    },
  });

Clarified this:

The modalVisible flag and the setModalVisible method to change it you suggested to use are present in my code with the modal state and the openModal() hook function which handles it, all in App component.

Do you see problems to trigger to
modal flag change back to false inside the Timeline.onReverseComplete() callback as I've done, maybe?

About this statement of yours:
You should create the GSAP instance in the initial render with:

useEffect(() => {
  // create GSAP instance here
}, [])

I've done that in the first place, I swear!!! :-)
A collegue of mine suggested me to move it outside...

In the meantime I'm goint to:

  • Try the Timeline.reversed() approach in place of the Timeline.onReverseComplete() callback to
  • Move my Modal component modal() method logic inside the useEffect hook as you suggested

Keep in touch :-)


 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...