Framer Motion not updating scrollYProgress - css

I'm using Framer Motion's useViewportScroll hook to track the user's scroll on my page, but it isn't updating. I came across this article, which explains how Framer Motion uses document.body.clientHeight and window.innerHeight to calculate the viewport scroll.
The article explains how some CSS can break this, specifically if document.body.clientHeight - window.innerHeight <= 0.
I can't seem to figure out how this wouldn't be true. Even with no CSS at all in my react app, that expression evaluates to true. You can see it in this example on CodeSandbox.
Thanks!

I think the reason you're not seeing updates is not because of any css.
Framer seems to calculate it's state standalone from react. So when Framer updates its values internally. react won't necessarily trigger a re-render with the updated values. (probably this is done for performance).
You can hook into the state changes by subscribing to an onChange handler that Framer is exposing and set the state you're interested in.
For example, the onChange on scrollYProgress will call setState, triggering react to re-render.
import React from "react";
import { useViewportScroll } from "framer-motion"
const FramerPostionHook = () => {
const { scrollYProgress } = useViewportScroll();
const [hookedYPostion, setHookedYPosition] = React.useState(0);
React.useEffect(()=>{
// hook into the onChange, store the current value as state.
scrollYProgress.onChange(v=> setHookedYPosition(v));
},[scrollYProgress]); //make sure to re-subscriobe when scrollYProgress changes
return (<>
scrollYProgress.current: {scrollYProgress.current}<br/>
scrollYProgress.hookedYPostion: {hookedYPostion}<br/>
</>)
}
Sandbox example

Related

changing css code and setting focus at the same time (why doesn't it work?)

I tried to make...
There is no input on the screen at the first rendering. When I click the button, input appears. And I want to set focuse on the input at the same time.
Let me explain what i made.
At first, the input is not visible on the screen.
Because the display property of the Box(the div tag), which is the parent component of the input, is none.
But when i click the button, the display property of the Box changes to block.
And here is what i want to do.
i'm going to set focus on the input on the screen.
In the function called when the button is clicked, I wrote a code that changes the css code and sets the focus on the input.
But it didn't work.
Please take a look at the following code.
const [inputDisplay, setInputDisplay] = useState("none");
const refInput = useRef(null);
const HandleShowInput = () => {
setInputDisplay("block");
refInput.current.focus();
};
return (
<>
<Box theme={inputDisplay}>
<Input ref={refInput}/>
<Box/>
<Button onClick={HandleShowInput}/>
</>
)
Below is the code that is dynamically changing the css of the Box component.
import styled, { css } from "styled-components";
const Box = ({ children, ...props }) => {
return <StBox {...props}>{children}</StBox>;
};
const StBox = styled.div`
${({ theme }) => {
switch (theme) {
case "block":
return css`
display: ${theme} !important;
`;
default:
break;
}
}}
`;
export default Box;
But this below code is worked. I separated the code by putting it in useEffect.
const [inputDisplay, setInputDisplay] = useState("none");
const refInput = useRef(null);
const HandleShowInput = () => {
setInputDisplay("block");
};
useEffect(() => {
refInput.current.focus();
}, [inputDisplay]);
return (
<>
<Box theme={inputDisplay}>
<Input ref={refInput}/>
<Box/>
<Button onClick={HandleShowInput}/>
</>
)
I want to know why the upper case not works and the lower case works. I don't know if I have lack react knowledge or css knowledge. I would be very grateful if you could help a beginner in react. Also, please understand if there are any unnatural sentences because i'm not good at English. thank you.
When you are trying to focus on the input element by HandleShowInput this function.Here two things are happening your changing the state and focus of input.It will focus the input but time will be so less that we can't see on the ui.And also due to the state change render will happen and again ref will get the input element. Thus you are not able to see this focussed input
But in case of useEffect this will happen after the render. After this no rendering. So we can see the focussed input
The way of thinking about React is a little different from Javascript.
You may expect the below two run in the same way.
setInputDisplay("block");
refInput.current.focus();
and
document.querySelector('#canFocus').style.display='block'
document.querySelector('#canFocus').focus();
NO~ It's not.
JS block the Dom and then focus it, it works well.
But React works like the code below.
setTimeout(()=>{
// next react render cycle callback
document.querySelector('#canNotFocus').style.display='block'
}, 1000)
document.querySelector('#canNotFocus').focus();
While focus method is called, the dom is display as none;
You set state in react, ReactDom will make it as a display block in the next life cycle of function component.
demo here : https://codesandbox.io/s/confident-wilson-q01ktj?file=/index.html
useEffect(() => {
refInput.current.focus();
}, [inputDisplay]);
is a watching function. While inputDisplay changed, the function inside will be called.
you set state to block
react re-render the component as a newer state
render function called, and dom is block
Effect watching function is called and the focus() called.

How to get image height and width properties in Next.js?

I just want to know how can we let an image take its own height and width using <Image> component in Next.js, instead of defining height and width by ourselves?
Beginning from Next.js v11, you can simply do:
import Image from 'next/image';
import foo from 'path/to/foo.png';
// ...
<Image src={foo} alt="" /> // <-- no need to pass height/width
In the above example, foo will be an object containing src, height, width (and optionally blurDataURL). So, if you want to access them, you can do stuff like foo.height, etc.
Also, there are a few hacks that may interest you:
Use layout="fill" and pass a handler to onLoadingComplete, where you will get naturalWidth and naturalHeight. Then you can set the state to adjust the container accordingly. But this approach is not recommended as it will likely increase CLS.
In data fetching methods [getStaticProps (static generation) / getServerSideProps (SSR)], read your image files and pass their dimensions as a prop to your components. SSR may cause an overhead on your server resources, but that can be minimized significantly by caching.
Simply use your good old img tag. Again not recommended, but will work if your files are already optimized. ESLint may throw some warning depending on your configuration, you would need to disable those rules.
PS: Just check once if your use-case can be handled by layout="fill" (without updating state).
Just a clarification - on older threads on SO, GitHub, etc., you may see an unsized prop being passed to next/image. It was deprecated in Next.js v10, and removed in v11. So, its probably not gonna work for you.
References:
https://nextjs.org/docs/api-reference/next/image
https://nextjs.org/docs/basic-features/data-fetching
https://nextjs.org/docs/going-to-production
Get the width and height of an image in node.js
Next.js build fails using <img> tag
How to use Image component in Next.js with unknown width and height
https://web.dev/cls
https://web.dev/time-to-first-byte
A sample implementation of the second hack:
import fs from "fs";
import path from "path";
import Image from "next/image";
import probe from "probe-image-size";
const IndexPage = ({ mountains }) => (
<Image
src="/mountains.jpg"
height={mountains.height}
width={mountains.width}
alt="Mountains"
/>
);
const getStaticProps = async () => {
const mountains = await probe(
fs.createReadStream(path.join(process.cwd(), "public/mountains.jpg"))
);
return { props: { mountains } };
};
export default IndexPage;
export { getStaticProps };

Contextual styling of ReactJS styled-components

I'm running into problems with Styled Components, and I'm not sure if it's a technical limitation, or if my mental model of how to use them is wrong.
In this example (I just wrote it quickly in Codepen, so disregard spelling mistakes) I have a component that is a username in a span. I want to be able to use that component anywhere, so it's styling is pretty minimal:
// From User.js
import React from 'react';
import styled from 'styled-components';
const Username = styled.span`
color: black;
`
const User = () => (
<UserName>bla</Username>
);
export default User;
I want to be able to use this span in a different component, but in that case I want it's styling to be overridden in this context:
// From Userblock.js
import React from 'react';
import styled from 'styled-components';
import User from './user';
const UserWrapper = styled.div`
// Some styles
`
const User = styled(User)`
color: red; // Different color when displayed in a UserBlock
`
const UserBlock = () => (
<UserWrapper>
<User />
</UserWrapper>
);
export default UserBlock;
From what I know, that's the way to set styling on a third party component (and that works elsewhere in my application), and it should work as far as I can tell, but I get nothing.
Also, going back to the mental model question - is this the way you're supposed to do it when you're using styled-components?
Thanks for any help!
When you wrap a normal React component with styled(X) you need to make sure to attach the class that styled-components passes in.
This means for your example, this will solve the issue:
const User = (props) => (
<UserName className={props.className}>bla</Username>
);
When you override the styles of an existing component styled-components goes ahead, generates a class, injects it and passes it to the component. That means if you don't attach the class to anything you'll never see the styles though! (see the documentation for more information)

Adding class to React Component after a certain amount of time

I have a react component:
React.createComponent({
render: function () {
return (<div className="some-component"></div>);
}
});
Some seconds after it renders, I want it to add a class from within the component. The class is for the purposes of revealing the component with an animation. I don't regard it as a real change of state, as it has no affect on the application other than to give the component an animated introduction, so I'm loathed to initiate it from outside of the component via a change of store/state.
How would I do this in simple terms? Something like:
{setTimeout(function () {
this.addClass('show'); //pseudo code
}, 1000)}
Obviously I could use jQuery, but that feels anti-React, and prone to side-effects.
I don't regard it as a real change of state, as it has no affect on the application other than to give the component an animated introduction
A change of state in the component seems the natural and proper solution for this scenario. A change in a component state triggers a re-render, which is exactly what you need here. Consider that we're talking about the state of your component, not of your application here.
In React, you don't deal with the DOM directly (e.g. by using jQuery), instead your component state should "drive" what's rendered, so you let React "react" to changes in state/props and update the DOM for you to reflect the current state:
React.createComponent({
componentDidMount () {
this.timeoutId = setTimeout(function () {
this.setState({show: true});
}.bind(this), 1000);
}
componentWillUnmount () {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
}
}
render: function () {
return (<div className={this.state.show ? 'show' : null}></div>);
}
});
When using setTimeout in React you need to be careful and make sure to cancel the timeout when the component gets unmounted, otherwise your timeout callback function will run anyway if the timeout is still pending and your component gets removed.
If you need to perform an initial mount animation or more complicated animations, consider using ReactCssTransitionGroup, which handles timeouts and other things for you out of the box: https://facebook.github.io/react/docs/animation.html

react css animation when component mounts

Here's a fiddle of what I want to do: https://jsfiddle.net/s7s07chm/7/
But I want to do this with react instead of jquery. Basically, I put the className of the element in the state, and on componentDidMount I update the className to initiate the transition.
But this isn't working. The component is just rendering with the transitioned state. In other words, instead of sliding down, it appears at the bottom from the beginning
Am I doing this wrong?
If so, is there another way to accomplish this?
here's the actual code
getInitialState: function() {
return {
childClass: 'child'
};
},
componentDidMount: function() {
this.setState({
childClass: 'child low'
});
},
the reason that won't work is because your DOM won't be updated until the component is mounted. So the class you're assigning with getInitialState will never appear in the DOM, but the one you set with componentDidMount will. As Ray mentioned, you should take a look at ReactCSSTransitionGroup.
componentDidMount is called after React hands them off to the browser, but the browser might not have finished drawing.
You can however use requestAnimationFrame to ensure setState is called after the browser has finished drawing the component.
componentDidMount() {
requestAnimationFrame(() => this.setState({
childClass: 'child low'
}))
}

Resources