React - Framer motion: prevent initial prop from triggering - reactjs

I have a basic CRUD application where I'm able to add and remove items on a list, which is animated. This works well with framer-motion, but I'm having troubles with the following:
Whenever I delete an item from the list, the following function is called:
const handleDeleteFormation = async (id) => {
await axios({
method: "DELETE",
url: `http://localhost:1337/formations/${id}`
})
getFormations()
}
As you can clearly see, I'm calling the getFormations() function after the deletion to re-render the updated list:
async function getFormations() {
const res = await axios.get('http://localhost:1337/formations')
setFormations(res.data)
console.log(res.data)
}
This will results in a re-render of the list, which is good. But... right after the re-render, the initial prop of the motion.div will be triggered again which results in conflicting visuals.
<AnimatePresence exitBeforeEnter initial={false}>
{formations && formations.map((formation, index) =>
<motion.div
key={formation.id}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -10 }}
transition={{ type: "tween", ease: 'easeOut', duration: 0.2 }}
>
...
My question: is it possible to prevent the initial prop from triggering right after the list-update?

You're looking for <AnimatePresence initial={false}>.
From the docs:
Suppressing initial animations
By setting initial={false} on AnimatePresence, components present when
AnimatePresence first loads will start in their animate state. Only
components that enter after this initial render will animate in.

Related

React - Product Fly to cart animation using framer-motion

i want an animation that takes the product Card and put it on the card like a fly animation and i want to use the framer-motion the problem that i have is when i set the x,y of the animation all of them goes to the same place
i tried the GSAP library but i didn't know how to manage it.
if there's any other libraries that i can use with explaination feel free to share
Here's my code :
const [xValue, setxValue] = useState(0);
const [yValue, setyValue] = useState(0);
const handleClick = async (p) => {
await set_order({
...order,
products: {
...order?.products,
[order?.products?.length]: { ...p, qte: 1 },
},
});
setxValue(45);
setyValue(45);
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1, x : xValue , y : yValue }}
transition={{ duration: 1 }}
exit={{ opacity: 0 }}
>
<Card
// ref={ref_card}
onClick={() => handleClick(product)}
i tried the gsap library and now im trying the framer motion
couldn't find that much resources that have this kinda of animation
i only found one that uses the gsap and i couldn't apply it
thank you for sharing any informations regarding this issue

How to add animations between pages in gartsby with Gsap

I want to add animation between pages in gatsby using Gsap.
Im trying to follow their docs: https://www.gatsbyjs.com/docs/how-to/adding-common-features/adding-page-transitions-with-plugin-transition-link/
At some point they say: Finally, import the TransitionLink component wherever you want to use it...
https://www.gatsbyjs.com/docs/how-to/adding-common-features/adding-page-transitions-with-plugin-transition-link/#getting-started
But I dont know where to use, should it be used in the actual pages components or in the Layout component?(I want to have disfferent animations for each page though)
I want to use the trigger function:
https://www.gatsbyjs.com/docs/how-to/adding-common-features/adding-page-transitions-with-plugin-transition-link/#using-the-trigger-function
Because I want to use Gsap.
But then they just write in the code "someCustomDefinedAnimation" but I dont know where is that coming from, how do i create one and how do I pass it through props.
<TransitionLink
exit={{
length: length,
trigger: ({ exit, node }) =>
this.someCustomDefinedAnimation({ exit, node, direction: "out" }),
}}
entry={{
length: 0,
trigger: ({ exit, node }) =>
this.someCustomDefinedAnimation({ exit, node, direction: "in" }),
}}
{...props}
>
{props.children}
</TransitionLink>
So my questions are:
Where should I add the TransitionLink component?
How do I create a custom Gsap animation?
Where and how do i pass animation this as props so that TransitionLink can use it?
But then they just write in the code someCustomDefinedAnimation but
I dont know where is that coming from, how do i create one and how do
I pass it through props.
Well, someCustomDefinedAnimation is just a defined function that contains the animation. For example:
verticalAnimation = ({ length, direction}) => {
const directionTo = direction === 'up' ? '-100%' : '100%'
const directionFrom = direction === 'up' ? '100%' : '-100%'
// convert ms to s for gsap
const seconds = length
return gsap.timeline()
.set(this.transitionCover, { y: directionFrom })
.to(this.transitionCover, {
y: '0%',
ease: "power1.easeInOut",
duration: seconds / 2,
})
.set(this.layoutContents, { opacity: 0 })
.to(this.transitionCover, {
y: directionTo,
ease: "power1.easeIn",
duration: seconds / 2,
})
}
Then you'll need to use it as:
<TransitionLink
exit={{
length: length,
trigger: ({ exit, node }) =>
this.verticalAnimation({length: 100, direction: 'up'}),
}}
entry={{
length: 0,
trigger: ({ exit, node }) =>
this.verticalAnimation({length: 100, direction: "down" }),
}}
{...props}
>
{props.children}
</TransitionLink>
Basically, it allows you to fully customize the function triggered in each page transition
You can find a detailed explanation and walkthrough in TransitionLink docs and an implementation example at their GitHub repository.

why would changes to the Ref object trigger rerender in this code

as shown in the react-native animation demo on https://reactnative.dev/docs/animated
const fadeAnim = useRef(new Animated.Value(0)).current // Initial value for opacity: 0
React.useEffect(() => {
Animated.timing(
fadeAnim,
{
toValue: 1,
duration: 10000,
}
).start();
}, [fadeAnim])
return (
<Animated.View // Special animatable View
style={{
...props.style,
opacity: fadeAnim, // Bind opacity to animated value
}}
>
{props.children}
</Animated.View>
);
}
the changes to the fadeAnim would trigger rerender of the component, which I don't understand.
the react documentation never mentions the ref object is part of state either.
am I missing something?
Im not sure but I think it will still work even if you remove fadeAnim in the dependency of useEffect. I never tried using refs as a dependency of hooks before and it works as expected.

React Router + React Pose slide in/out with "POP" or "PUSH" direction

I've been having this issue for over a month, getting back to it with new ideas over and over, but I can't wrap my head around a solution. There must be one out there, this is trivial!
My goal is to normally slide pages in from left and slide out to right. When it reverses (user click back button or history notices a route has been popped) the page should exit to the right and next route should enter from left (opposite).
Everything works, but when I PUSH a route and then POP a route, the next page will both exit to right and enter from right. The entering page uses an old pose before it has time to update. If I POP another time, it then works correctly, and the new page slides from left while the old page exits to right. It's only the first time I change with a POP. I'm very dependent on the const reverse value. The component only renders once when I change route and reverse = true the first time I click POP.
I have an AnimatedSwitch component like this
export const AnimatedSwitch = ({ location, children, ...rest }) => {
const clickedBack = useSelector(state => state.interfaceReducer.clickedBack);
const routePopped = useSelector(state => state.routeReducer.routePopped);
const reverse = clickedBack || routePopped;
const onRest = () => {
clickedBack && dispatch(toggleClickedBackButton(false));
}
return (
<PoseGroup
animateOnMount={true}
preEnterPose={reverse ? "leftSide" : "rightSide"}
exitPose={reverse ? "rightSide" : "leftSide"}
onRest={onRest}
reverse={reverse}
>
<ContextRouteAnimation poseKey={`${reverse}-${location.pathname}`} key={location.pathname} reverse={reverse}>
<Switch location={location} {...rest}>
{children}
</Switch>
</ContextRouteAnimation>
</PoseGroup>
)
}
And this is the ContextRouteAnimation pose component:
const ContextRouteAnimation = posed.div({
enter: {
opacity: 1,
x: "0%",
transition: {
type: "tween",
ease: "easeInOut",
duration: TIMING.SLOW,
},
},
leftSide: {
x: "-100%",
opacity: 0,
transition: {
type: "tween",
ease: "easeInOut",
duration: TIMING.SLOW,
},
},
rightSide: {
x: "100%",
opacity: 0,
transition: {
type: "tween",
ease: "easeInOut",
duration: TIMING.SLOW,
},
},
});
I've tried multiple things such as
Pass reverse down to ContextRouteAnimation and use preEnter + exit poses.
Set pose directly on ContextRouteAnimation
Store component as state and only update it after new pose is updated, and then trigger another render

Modal component renders Twice on open

I'm using react-spring to animate a Modal based on #reach/dialog. The Modal can have any children. In the children I'm fetching some data based on some prop.
The problem is that the fetch call is made two times on opening the modal. I think it has probably to do with how I'm managing the state and that is causing re-renders.
I'v tried memoizing the children inside the modal and it didn't work, so I think that the problem is outside of the Modal component.
Here is something close to my code and how it is working https://codesandbox.io/s/loving-liskov-1xouh
EDIT: I already know that if I remove the react-spring animation the double rendering doesn't happen, but I want to try keeping the animation intact.
Do you think you can help me to identify where is the bug? (Also some tips on good practice with hooks are highly appreciated).
it renders three times because your return component has transitions.map since you have three item inside the
from: { opacity: 0 }
enter: { opacity: 1 }
leave: { opacity: 0 }
the {children} was called two times when the isOpen is true
you can fix the issue with just removing the from: { opacity: 0 } and leave: { opacity: 0 }
so change your modal.js => transitions
const transitions = useTransition(isOpen, null, {
enter: { opacity: 1 }
});
I checked and it is rendered twice because of animation in a Modal component when an animation is finished, modal is rendered second time when I commented out fragment responsible for animation, Modal renders only once.
const Modal = ({ children, toggle, isOpen }) => {
// const transitions = useTransition(isOpen, null, {
// from: { opacity: 0 },
// enter: { opacity: 1 },
// leave: { opacity: 0 }
// });
console.log("render");
const AnimatedDialogOverlay = animated(DialogOverlay);
// return transitions.map(
// ({ item, key, props }) =>
// item && (
return (
<AnimatedDialogOverlay isOpen={isOpen}>
<DialogContent>
<div
style={{
display: `flex`,
width: `100%`,
alignItems: `center`,
justifyContent: `space-between`
}}
>
<h2 style={{ margin: `4px 0` }}>Modal Title</h2>
<button onClick={toggle}>Close</button>
</div>
{children}
</DialogContent>
</AnimatedDialogOverlay>
);
// )
// );
};
The problem is, that at the end of the animation AnotherComponent remounts. I read similar problems about react-spring. One way could be, that you lift out the state from AnotherComponent to the index.js. This way the state will not lost at remount and you can prevent refetching the data.
const AnotherComponent = ({ url, todo, setTodo }) => {
useEffect(() => {
if (todo.length === 0) {
axios.get(url).then(res => setTodo(res.data));
}
});
....
}
Here is my version: https://codesandbox.io/s/quiet-pond-idyee

Resources