Framer Motion Library "hooks" not working with reagent - reactjs

The path tag of framer motion can be used in conjunction with useViewportScroll to create a scroll info path.
const { scrollYProgress } = useViewportScroll()
return (
<motion.path style={{ pathLength: scrollYProgress }} />
)
When used with Clojurescript, this doesn't work:
(def div (.-div motion))
(def path (.-path motion))
(defn my-component []
[:> div
[:> path {:style {:pathLength (.-scrollYProgress (useViewportScroll))}}]]
)
The error is:
Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See for tips about how to debug and fix this problem.
-- EDIT --
Based on an answer, I changed to this:
(defn path []
(let [path (.-path motion)
path-length (.-scrollYProgress (useViewportScroll))]
(r/as-element
(do
(js/console.log "path length is " path-length)
[:> path {:style {:pathLength path-length}}]
))))
and use [:> path] in my-component. But the console log isn't triggered when I scroll the page, suggesting that the path-length variable isn't changing when scroll, i.e., the path component isn't remounted with the scroll. How to fix this?

Hiccup style components won't work with Hooks, because in order to interpret the returned hiccup, Reagent creates a class component, not a functional component (Frankly this is probably a historical oddity as Reagent pre-dates the hooks API).
Instead you need to create a functional component by passing reagent.core/create-element a function, but in that function you either will have to create React DOM elements by hand, or convert the hiccup yourself. See: https://github.com/reagent-project/reagent/blob/master/doc/ReactFeatures.md#function-components

Related

Alternative to Reactdom.render and unmountComponentAtNode in react18

Important note:
I am aware of createRoot and root.unmount()! Unfortunately (If I understand this correctly) they should be used just once in the application for mounting the react application.
Problem description:
In our app we have a modal component that is rendered dynamically and added to the body of the html via ReactDOM.render(). When this modal is hidden, we unmountComponentAtNode().
Unfortunately, after upgrading to react18, unmountComponentAtNode becomes deprecated and the new unmount is (in my understanding) for the root only. The same problem is about if I try to modify the ReactDOM.Render() for createRoot. Then we would have 2 roots in the app which is wrong.
What is the proper way to attach the modal to the body element (next to root!) and unmount it after it should be destroyed? The implementation is a little bit "weird" (partially in jsx, partially not...) and I would like to avoid refactoring the whole component as there will be a lot of refactoring already in the code... So I would like to focus on refactoring this component (into jsx one) later. Now I have to figure out only the rendering / unmounting. I have been thinking about using Portals, but anyway I have to create that elements somehow and render them into the DOM where portals does not help me a lot.
Calling the createRoot and then render on the root in this modal component fires an error You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it. which is obvious. But there is no "useRoot()" hook or anything like that. Should I store the returned object (root) in some context or somewhere to use it later? Or what should be the best option to call the render? :/
I know how I should do that with classical functional component... But maybe there is some way that I can just refactor a piece of the code instead of the whole component and all its usecases. Maybe there is something I am not aware of (there is definitely thousands of things I am not aware of :D) that should simplify my life...
function modal() {
return (
<div>
...
</div>
)
}
Modal.show = () => {
modalEl = document.createElement('div');
util.destroy(el) => {
ReactDOM.unmountComponentAtNode(el);
el.remove();
}
const childs = props.childs;
REactDOM.render(childs, modalEl);
}
When I was thinking about portals, I thought I will just rewrite the last line of ReactDOM.render to portal like createPortal(childs, modalEl), unfortunately this does not render anything (except modalEl, but no childs inside). The childs are of type ReactNode (using typescript) and they are not empty (because of ReactDOM.render works without any problem).

How does React provide independent contexts for subtrees?

If, as a thought experiment, I were to write my own createElement implementation for JSX, what might support for implicit context look like?
In particular, I can't figure out how with the limited means of JSX's createElement signature, contexts can be independent for different subtrees. (It appears React's Context handling has become more elaborate in recent versions; I'm mostly interested in the seemingly more straightforward mechanisms of earlier versions.)
This might be used to automatically determine heading levels, for example:
<Section title="Hello World">
<Card title="Details" />
</Section>
<Card title="Example" />
Here Card would automatically generate <h3> and <h2>, respectively, by relying on something like context.headingLevel.
A very nice question, that shows how different is the concept of creating React Elements to actually executing the render functions (either the .render method of class components or simply the main body of a functional component).
In JSX itself (which is just React.createElement(…)) there‘s no concept of “context” at all. It comes into existance only when the components are rendered. It is indeed a duty of the React Renderer (such as React DOM or React Native) to actually implement Context APIs.
If you remove the ability to store states and to update the UI you are left with a minimal React implementation that only “renders once”, but perfectly fine to understand the problem at hand.
Everytime the React Renderer needs to render a React Elements tree (such as one built with JSX) it passes every single element and transforms it into a DOM structure, but when it encounters a component node (not a “native” element) it needs to render it to obtain its React Element sub tree, and swap the original node with it.
It’s in this specific moment that React can keep track of which Context values to pass to which components, since it is traversing the tree.
So, to answer directly your question, you can’t implement context in the “element creation phase”, inside your JSX implementation, you need to do it in a subsequent phase when you can traverse the tree.
If you were trying to build an “immediate JSX” you probably have something like this:
function createElement(type, props, ...children) {
props = { children, ...props };
if (typeof type === 'function') {
return type(props);
} else {
return { type, props };
}
}
In thise case you will not be able to implement an API similar to context, because the execution order is inner-then-outer:
const div = createElement('div', {}, createElement(Card, {}));
// identical to
const card = createElement(Card, {}); // inner, and then…
const div = createElement('div', {}, card); // outer

What does "animated" do in react-spring?

I am currently getting to grips with the react-spring animation library.
In some of the CodeSandbox demos (e.g. https://codesandbox.io/embed/j150ykxrv) in the documentation, something is imported called "animated":
import { Transition, animated } from 'react-spring'
and then used like so:
{this.state.items.map(item => styles => <animated.li style={{ ...defaultStyles, ...styles }}>{item}</animated.li>)}
In other examples this isn't used:
import { Spring } from 'react-spring'
<Spring
from={{ opacity: 0 }}
to={{ opacity: 1 }}>
{props => <div style={props}>✌️</div>}
</Spring>
I can't find any mention in the documentation of what this does or why it is used, as it seems you can animate by just passing animated style props into a component.
Are the uses in the documentation part of a legacy version?
Native is optional, if you set it (and then you need the component to extend from animated.xxx) it won't render out the animation like normally react animation libs would do, in other words they call forceUpdate or setState on every frame, which is expensive. With native it will render the component once, then animate in the background using a requestAnimationFrame-loop which sets the styles directly. The values you pass to your target div (or whatever) are not numeric values but classes that receive update events, this is why you need to extend.
Rendering through react is not obsolete, though, it is the only way you can animate React component props. If you have a foreign control, or a D3 graph for instance, you would simply blow props into it while the spring renders them out.
Looking further into the docs, I can see it is used for "native" rendering.
This allows react-spring to bypass React for frame updates. The benefits of this method are improved performance.
It is recommended to use this approach
"Try doing this in all situations where you can"
Just be aware of the following conditions:
native only animates styles, attributes and children (as textContent)
The values you receive are opaque objects, not regular values
Receiving elements must be animated.[elementName], for instance div becomes animated.div
If you use styled-components or custom componemts do: animated(component)
If you need to interpolate styles use interpolate
Summarised benefits:
Your application will be faster, the difference really can be night
and day
You get quite powerful interpolation and keyframing tools (see
below)
You get to animate scrollTop and scrollLeft out of the box (which
React can't normally handle)
Looks like it is used for doing native rendering,take a look a the Transition component , it has a native prop

react animation with gsap without react-transition-group and ref

I have a question with GSAP and react, as I read from some tutorials, they all use react-transition-group and also many of them use ref as an alternative selector for GSAP how ever, if I use ref in my case, the whole page will be animated, I just want a single element to animate so I use id selector, and it works totally fine like this
import React from 'react';
import { TweenMax } from 'gsap';
import uuid from 'uuid';
import '../styles/homePage.css';
class HomePage extends React.Component{
startAnimation=(pic)=>{
TweenMax.from(`#${pic.id}`, 1, {
opacity: 0,
x: -100,
y: -100
});
}
render(){
const PicsNum = 15;
let pics = [];
let pic = {};
for (let i = 5; i <= PicsNum; i++) {
const picPath = `/pictures/testingPics/${i}.jpg`
pic={id:`a${uuid()}`, picPath}
pics.push(pic)
}
const renderPics = pics.map((p, i) => (
<div
key={i}
className='img-container'
>
<img src={p.picPath} className='pic' id={p.id}/>
<button onClick={()=>{this.startAnimation(p)}}>click</button>
</div>
))
return (
<div className='pics'>
{renderPics}
</div>
)
}
}
export default HomePage;
can someone please tell me why should I use react-transition-group and what can go wrong if I want to use animation without it like I am doing? thank you very much
So, what you are doing here is absolutely fine for simple animations. It's only when your logic and animations start becoming more complicated that you may find it has downsides.
The main problem you may encounter as the complexity of your logic / animation increases is that you actually are now using two different libraries to target the dom. React wants to be completely in control of the dom so it can do its thing. GSAP however also is now looking for an element in the dom and controlling it's state, and react doesn't know about so now things might get out of sync. React might re-render that component, resetting your opacity to 1, when the user has already triggered the fade out.
React-transition-group can be a useful tool in simplifying working with animating components in and out, but it is not the only way to do it or the be all and end all of react animation, so don't feel like you have to use it. Just maybe look into the ways in which is simplifies the code you have to write for every component you want to animate in or out. (It gives you specific lifestyles for animating in and out, and a callback to remove the component post animation, which is the bulk of the boilerplate for component transitions).
In the case of the first issue I mentioned Transition-group is useful here because all your animation code is wrapped within the helpers it provides, so react knows: 1)Your animating... don't do anything till you've finished... 2)now you've finished and I'm back in control.
But there are other options outside of transition group to deal with this dichotomy of dom control:
You can try to be super smart and declarative about it... use refs to access the elements and pass them to gsap animations that are triggered and controlled by state/props.
But there are brilliant libraries that will take all the hassle out of worrying about state and animation and things like https://github.com/azazdeaz/react-gsap-enhancer
This is a wonderful higher order component that just makes sure any changes that gsap makes to the elements are noticed and preserved across react re-rendering and state changes.
Honestly it's a bit magic, and makes working with react and GSAP an absolute pleasure.
Also to answer your question about 'Why refs' instead of the useful 'just pass a string of the ID to the gsap function':
There isn't a right in wrong here. A ref in react will store a pointer to that Dom element in memory. Making it a convenient lookup. Its main advantage is the reference to that element will not expire upon a react re-render. If you manually select an element using GetElementById, and that Dom node is replaced by a react re-render, then your variable reference will become undefined and you'll have to call GetElementById again. GetElementById is very cheap in performance terms, it's not about performance, just avoiding the boilerplate of having to 'find' a new reference to the Dom element after every re-render.

Reagent generate a React Component that passes React.isValidClass(component)?

I'm trying to use react-router in my Clojurescript Reagent project. The problem is, react-router requires that components pass React.isValidClass(component), which in React 0.11.2 is defined as:
ReactDescriptor.isValidFactory = function(factory) {
return typeof factory === 'function' &&
factory.prototype instanceof ReactDescriptor;
};
Reagent seems to generate components as an object instead of a function. Here is my code:
(defn home []
[:div [:h1 "Home Page placeholder"]])
(reagent/as-component (home)) ; => #<[object Object]>
Has anyone worked out how to make this sort of interop work?
What reagent-react-router does to make this work is use reagent.core/reactify-component. The reactify-component exists to make Reagent components valid React components for these kinds of inter-op scenarios.

Resources