Animation resets states of child components

Viewed 89

I have a framer motion component (I've tried with Stitches components too) that animates to and from view based on a useState variable:

const [visible, setVisible] = useState(true);

<motion.div
    animate={visible ? "one" : "two"}
    variants={{
      one: {
        transform: "translateX(0%)",
        opacity: 1
      },
      two: {
        transform: "translateX(100%)",
        opacity: 0
      }
    }}
  >
  // children
</motion.div>

The children components are wrapped in contexts, and the problem is that their states reset every time the animation is triggered.

I've tried declaring the animation component outside of its current scope. In its current scope it resets the states of everything except the "currentUser" state in my AuthProvider context. Outside the scope it resets "currentUser" too. I've decided not to include the original code for brevity.

Here is a minimum reproducible. If you type something in the input, and then click the toggle button, the input is lost.

2 Answers

DummyInputComponent should be stateless, use the value provided by the context and also have a onChange handler function passed on from the context

Something like this:

export function AuthProvider({ children }) {
  const [dummyValue, setDummyValue] = useState(null);

  const handlechange = (e) => {
    setDummyValue(e.target.value);
  };

  const value = {
    dummyValue,
    onChange: handlechange
  };

  const DummyInputComponent = () => {
    return <input value={value.dummyValue || ""} onChange={value.onChange} />;
  };

  return (
    <AuthContext.Provider value={value}>
      <DummyInputComponent />
    </AuthContext.Provider>
  );
}

Working CodeSandbox

I know the common use case for this is for performance tweaking, but I ended up avoiding the rerenders caused by framer motion (supposedly) by using memoization:

const Memoized = React.memo(({ children }) => {
  const [visible, setVisible] = useState(true);

 return (
  <motion.div
    animate={visible ? "one" : "two"}
    variants={{
      one: {
        transform: "translateX(0%)",
        opacity: 1
      },
      two: {
        transform: "translateX(100%)",
        opacity: 0
      }
    }}
  >
    {children}
  </motion.div>
 );
});

I don't know if this is a good way to do it, so I'll leave this open.

Related