I have a custom modal which slides in upon componentDidMount
:
componentDidMount() {
Animated.timing(this._animatedValue.y, {
duration: 200,
toValue: 0
}).start()
}
Ok, this was easy. However, I also like to slide the modal out when the component unmounts. For me, componentWillUnmount()
would feel right, as it is an elegant declarative way:
componentWillUnmount() {
Animated.timing(this._animatedValue.y, {
duration: 200,
toValue: deviceHeight
}).start()
}
But this does of course not work, because the React doesnt wait until I have finished my animation.
So currently I work around this with a custom function:
closeModal() {
Animated.timing(this._animatedValue.y, {
duration: C.filterModalDuration,
toValue: deviceHeight
}).start()
InteractionManager.runAfterInteractions(() => {
this.props.UnmountThisComponent()
})
}
This is of course not as elegant, but it works. However, the pain begins if I need to call this function from a component far down in the component tree, i.e. I need to manually pass down this function via onUnmount={()=> closeModal()}
and then with onUnmount={this.props.onUnmount}
over and over again...
Then I thought that I could solve this with redux & redux-connect. I was thinking to trigger a redux state change from the child components, which would then call the function via componentWillRecieveProps()
like this:
componentWillReceiveProps (nextProps) {
if (nextProps.closeFilter === true) {
this.closeModal()
}
}
However this feels pretty hacky and imperative.
Is there any way to solve this problem in an elegant / declarative way?
A React component's lifecycle contains distinct phases for creation and deletion. In coding terms, these are called mounting and unmounting. You can also think of them as "setup" and "cleanup".
Mounting means putting elements into the DOM. React has four built-in methods that gets called, in this order, when mounting a component: constructor() getDerivedStateFromProps() render()
I would not use InteractionManager.runAfterInteractions
to execute after an animation completes. I recommend using the start
callback.
Animated.timing(this.animatedValue, {}).start(() => this.props.closeModal())
There are things like https://github.com/mosesoak/react-native-conductor which can help you coordinate deep animations and fire back up. This takes advantage of context
.
You could also use redux as you have tried however I would use componentDidUpdate
rather than componentWillReceiveProps
.
The reason is that it is only safe to cause a setState
in the component you have your componentWillReceiveProps
in.
If you trigger a dispatch
inside of componentWillReceiveProps
it will cause a setState
in other components cause your application to break.
Overall: I would recommend this flow. (Close Action Initiated) => Animate Modal Closed => in start(() => {})
callback cause a setState
or set a piece of data in your redux
store that will then unmount
your modal which is now hidden.
This way you get the unmount animation.
If you are using routing like react-navigation
I recommend setting up a mode: "modal"
navigation style. That way the unmounting/mounting modal animations are taken care of for you.
componentWillReceiveProps
seems to fit perfectly as it's intent is to make actions based on next props. This is ideal when parents want to trigger actions on their childrens (even through this is not really the pattern of React where children call parents functions, and parents receive events from childrens).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With