nandorojo / moti

🐼 The React Native (+ Web) animation library, powered by Reanimated 3.
https://moti.fyi
MIT License
3.9k stars 120 forks source link

Function passed to from prop of MotiView doesn't work #309

Closed kevingjs closed 10 months ago

kevingjs commented 10 months ago

Is there an existing issue for this?

Do you want this issue prioritized?

Current Behavior

Passing a function to from prop on MotiView component doesn't work, only works in exit prop.

Expected Behavior

Need to work with functions in from and exit props of MotiView to catch custom prop from AnimatedPresence component.

Steps To Reproduce

  1. Add a AnimatedPresence component.
  2. Set custom prop to AnimatedPresence.
  3. Add a MotiView child component to AnimatedPresence component.
  4. Add from and exit props to MotiView child.
  5. Set functions in from and exit props with console.log to check if functions are being executed.

Detailed example

const direction = 1;

<AnimatedPresence custom = { direction }>
    <MotiView
        from = {(direction) => {
            'worklet'
            console.log('test from prop'); // Doesn't work
            return { translateX: direction < 0 ? 1000 : -1000 }
        }}
        animated = {{
            translateX: 0
        }}
        exit= {(direction) => {
            'worklet'
            console.log('test exit prop'); // Work
            return { translateX: direction > 0 ? 1000 : -1000 }
        }}
        transition = {{
            type: 'timing',
            duration: 300,
            easing: Easing.out(Easing.ease)
        }}
    />
</AnimatedPresence>

Versions

- Moti: 0.26.0
- Reanimated: 3.3.0
- React Native: 0.72.3

Screenshots

No response

Reproduction

Minimal reproduction in Steps To Reproduce, StackBlitz is always crashing in my machine, sorry.

nandorojo commented 10 months ago

you can just pass an object to from

kevingjs commented 10 months ago

you can just pass an object to from

No man, I mean, the detailed example I gave you above in Expected Behavior is just an example of how the function passed to from prop doesn't work.

In my actual project, I don't have the MotiView child in the same scope as the AnimatedPresence component, so I can't just pass an object because that way, I can't get the latest data from the direction value.

I'm going to give you the closest code to my actual project (I can't write the exact code here because it's too long).

// FormInfo.js file
import { Easing } from 'react-native-reanimated';
import { MotiView } from 'moti';

const FormInfo = ({ currentStep }) => {
    return (
        <MotiView
            from = {(step) => {
                'worklet'
                console.log('test from prop'); // Doesn't work, just doesn't happen anything.
                return { translateX: step >= currentStep ? 1000 : -1000 }
            }}
            animated = {{ translateX: 0 }}
            exit= {(step) => {
                'worklet'
                console.log('test exit prop'); // Work
                return { translateX: step < currentStep ? 1000 : -1000 }
            }}
            transition = {{
                type: 'timing',
                duration: 300,
                easing: Easing.out(Easing.ease)
            }}
        />
    );
};

export default FormInfo;
// Form.js file
import React, { useState } from 'react';
import { AnimatePresence } from 'moti';
import FormInfo from '../utils/form_steps/form_info/FormInfo';
import FormEmail from '../utils/form_steps/form_email/FormEmail';
import FormPass from '../utils/form_steps/form_pass/FormPass';

const formSteps = [
    FormInfo,
    FormEmail,
    FormPass
];

const Form = () => {
    const [ step, setStep ] = useState(0);
    const CurrentForm = formSteps[step];

    return (
        <AnimatedPresence custom = { step } >
            <CurrentForm key = { `form_${step}` } currentStep = { step } />
        </AnimatedPresence>
    );
};

export default Form;

You see? The only way to pass the latest data of step is through the custom prop of AnimatedPresence.

Please help, if you need any more details, let me know.

nandorojo commented 10 months ago

In my actual project, I don't have the MotiView child in the same scope as the AnimatedPresence component, so I can't just pass an object because that way, I can't get the latest data from the direction value.

you could pass pass direction as a prop to the underlying component or use context, right? you can access this normally in from. exit is a special case and that’s why it needs a function prop

kevingjs commented 10 months ago

In my actual project, I don't have the MotiView child in the same scope as the AnimatedPresence component, so I can't just pass an object because that way, I can't get the latest data from the direction value.

you could pass pass direction as a prop to the underlying component or use context, right? you can access this normally in from. exit is a special case and that’s why it needs a function prop

That's right, pass direction to the underlying component works, I just thought the functions in the from prop also worked like in Framer Motion's CodeSandbox, nevermind, thanks.

nandorojo commented 10 months ago

framer accepts a function in the initial prop that passes custom down? didn’t know that, interesting. i worry it might add some overhead in our case (for iOS/Android) that may not be necessary, so probably better to avoid adding it here if possible