How to use useSprings? - javascript

I'm trying to create an animated text char by char to bounce from right to left, but I need to add a different delay to each char.
So far the animation looks nice the problem is that the delay is not updating for each char.
This is my code:
import React from "react";
import { useSprings, animated } from "react-spring"
import s from '../../styles/pages/home.module.scss'
// HERE CREATE AN ARRAY CHAR BY CHAR INCLUDING " ", FROM A STRING
let textString = "Random text for this example."
let textArray = Array.from(textString)
const HeroText = () => {
const springs = useSprings(
textArray.length,
textArray.map((item, i)=> ({
id : i,
from : {opacity: 0, translateX : '1000px'},
to: {opacity: 1, translateX : '0px'},
delay: (0.5 + i / 10),
config: { mass: 4, tension: 200, friction: 30}
}))
)
let elements = springs.map((spring, i) => {
console.log(spring)
return(
<animated.span key={i} style={{...spring}}>
{textArray[i] === ' ' ? <span> </span> : textArray[i]}
</animated.span>
)
})
return(
<div className={s.heroText}>
<h1 className={"my-heading divided-heading"}>
{elements}
</h1>
</div>
)
}
export default HeroText
On the console.log(spring), I can actually see that all the objects have different "delay" values, but on render they all animate at the same time, so it does not look like the text is animated char by char.
I have read the react-spring documentation but I did not find it to be very helpful, so if someone can help me understand what I'm missing I would be glad.
Thanks!

so after allot of research I found out that the best way to do what I was trying to do was to use "useTransition".
This was the final code, working properly.
import React from "react";
import { animated, useTransition } from "react-spring"
import s from '../../styles/pages/home.module.scss'
// HERE CREATE AN ARRAY CHAR BY CHAR INCLUDING " ", FROM A STRING
let textString = "Hi, I'm Stephane Ribeiro. Want to como on a full stack journey trough my mind?!"
let textArray = Array.from(textString)
let objArray = textArray.map((item, i) => {
return ({
char : item,
key: i
})
})
const HeroText = () => {
const tranConfig = {
from : {opacity: 0, translateX : '1000px'},
enter: {opacity: 1, translateX : '0px'},
config: { mass: 4, tension: 200, friction: 30},
trail: 50
}
const transition = useTransition(objArray, tranConfig)
let elements = transition((values, item) => {
return(
<animated.span key={item.key} style={values}>
{item.char === ' ' ? <span> </span> : item.char}
</animated.span>
)
})
return(
<div className={s.heroText}>
<h1 className={"my-heading divided-heading"}>
{firstElements}
</h1>
</div>
)
}
export default HeroText

How did you apply different delay time for each char. I have seen your last code there is no different delay time

Related

How to optimize images to imporve website performance in React.js

I am currently working on a website with the use of React.js. The website includes an automatic imageslider/carousel which is really effecting the performance of the website and slows it down significantly.
I have the following code for the image slider:
import React, { useState, useEffect } from "react";
import images from "../../constants/images";
import "../CSS/carousel.css";
import { BsArrowLeft, BsArrowRight } from "react-icons/bs";
const carouselImages = [
images.carousel_one,
images.carousel_two,
images.carousel_three,
images.carousel_four,
images.carousel_five,
images.carousel_six,
images.carousel_seven,
images.carousel_eight,
images.carousel_nine,
images.carousel_ten,
images.carousel_eleven,
images.carousel_twelve,
images.carousel_thirteen,
images.carousel_fourteen,
];
const Carousel = () => {
const [current, setCurrent] = useState(0);
useEffect(() => {
const slideInterval = setInterval(() => {
setCurrent((current) =>
current < carouselImages.length - 1 ? current + 1 : 0
);
}, 3000);
return () => clearInterval(slideInterval);
}, []);
const length = carouselImages.length;
const nextSlide = () => {
setCurrent(current === length - 1 ? 0 : current + 1);
};
const prevSlide = () => {
setCurrent(current === 0 ? length - 1 : current - 1);
};
if (!Array.isArray(carouselImages) || carouselImages.length <= 0) {
return null;
}
const switchIndex = (index) => {
setCurrent(index);
};
return (
<div className="app__carousel">
<div className="app__carousel-inner_container">
{carouselImages.map((slide, index) => (
<div
className={
index === current
? "app__carousel-slide_active"
: "app__carousel-slide"
}
key={index}
>
{index === current && (
<img src={slide} className="app__carousel-image" />
)}
</div>
))}
<BsArrowLeft onClick={prevSlide} className="app__carousel-left_arrow" />
<BsArrowRight
onClick={nextSlide}
className="app__carousel-right_arrow"
/>
</div>
<div className="app__carousel-indicator_container">
{carouselImages.map((_, index) => (
<button
className={`app__carousel-indicator_btn${
current === index ? " active" : ""
}`}
onClick={() => switchIndex(index)}
></button>
))}
</div>
</div>
);
};
export default Carousel;
I have already used the 'imagemin' plugin along with a craco.config.js file to try to optimize the images. My craco file looks like this:
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
module.exports = {
webpack: {
configure: (webpackConfig) => {
webpackConfig.optimization.minimize = true;
webpackConfig.optimization.minimizer.push(
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminMinify,
options: {
plugins: [["mozjpeg", { quality: 85 }]],
},
},
generator: [
{
preset: "webp",
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
"imagemin-webp",
"imagemin-pngquant",
"imagemin-svgo",
],
},
},
],
})
);
return webpackConfig;
},
},
};
Another thing that I have done is encoded the images with WebP by running
npm i imagemin-webp -D
and importing my images as shown below:
import carousel_one from "../assets/carousel_one.jpg?as=webp";
The steps which I have already taken have only improved the performance by a few points in the lighthouse testing. Is there anything else that can be added or imporved in the already existing code to improve the performance more?
You can further optimize images by using AVIF instead of WEBP, which in most cases should offer even better compression than WEBP with the same image quality. If you want to push this step further, there are AI compressors available online, that are able to shrink the size without loosing quality even further (This you will either have to do manually each time you want to shrink image size, or in most instances pay for API).
Another option is to serve images that are the minimum required size for the resolution you are using. This means either using <picture> or srcset attribute, or making your own solution in JavaScript.
But the most common solution to pictures slowing down page remain lazy loading.

Monaco editor prevent line decorations from expanding

My objective is to instantiate a Monaco editor line decoration that does not expand to the lower rows when I hit enter at the end of the created decoration.
For example, when I create a Monaco editor(in React) and instantiate a line decoration with the following code:
`js
import { createStyles } from "#mantine/styles";
import Editor from "#monaco-editor/react";
import monaco from "monaco-editor/esm/vs/editor/editor.api";
import { useRef, useState } from "react";
const DecoratedEditor = () => {
const { classes } = useStyles();
const [code, setCode] = useState(`#include <iostream>
using namespace std;
//press enter on my end to see decoration expand
int main() {
cout << "Hello World!";
return 0;
}`);
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
return (
<Editor
value={code}
language="cpp"
theme="vs-dark"
onChange={(newValue) => {
if (!newValue) return;
setCode(newValue);
}}
beforeMount={(monaco) => {}}
onMount={(editor, monaco) => {
editorRef.current = editor;
editor.getModel()?.deltaDecorations(
[],
[
{
range: new monaco.Range(4, 1, 4, 47),
options: {
inlineClassName: classes.lineDecoration,
},
},
]
);
}}
height={400}
/>
);
};
export default DecoratedEditor;
const useStyles = createStyles((theme) => {
return {
lineDecoration: {
width: "10px !important ",
backgroundColor: "rgb(255, 0, 0, 0.4)",
},
};
});
I get the following output: Normal editor with normal decoration
But, if I press "Enter" at the end of the decoration at line 4 and write on the following line I get this: New decoration
Is there a way to prevent the decoration from expanding itself? Thank you.
I already searched for options in Monaco editor documentation to prevent this from happening, but I didn't find out anythigh that could satisfy my needs.
I have been there.
It would help you get the job done as well.
That is
stickiness: 1
In you case, you can add one more option to the list.
options: {
inlineClassName: classes.lineDecoration,
stickiness: 1
}
stickness 1 is AlwaysGrowsWhenTypingAtEdges
/**
* Describes the behavior of decorations when typing/editing near their edges.
* Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior`
*/
export enum TrackedRangeStickiness {
AlwaysGrowsWhenTypingAtEdges = 0,
NeverGrowsWhenTypingAtEdges = 1,
GrowsOnlyWhenTypingBefore = 2,
GrowsOnlyWhenTypingAfter = 3
}
In case, a code snippet goes like this (you can use it as you would like) :
decorations = editor.deltaDecorations([],
[
{
range: new monaco.Range(1, 1, 4, 4),
options: {
stickiness: 1, // NeverGrowsWhenTypingAtEdges
}
}
])
}

Typescript Issue: From "or" to "specific" back to "or" (ReactJS example)

I guess the hardest part about this question was to find a suitable title 😅
So actually what I try to achieve is to render specific slides - but still.. every slide.. is actually a slide by data
lets look at what I mean and what I get from the api
type SomeSlideType = {
type: 'SomeSlide';
label: string;
headline: string
}
type SomeOtherSlideType = {
type: 'SomeOtherSlide';
members: number;
totalViews: number;
}
type AndAnotherSlideType = {
type: 'AndAnotherSlide';
someProp: string
someOtherProp: number;
}
type SlideType = {
sys: { id: string };
nextSlide: { id: string };
slide: SomeSlideType | SomeOtherSlideType | AndAnotherSlideType;
};
and in the end I get an array of slides => SlideType[]
So actually what I want to do is render them and still get the comfort of typesupport within the slides
What I NOT want to have, is some big chunk of if else cluster like that:
export const Example = ({ slides }: { slides: SlideType[] }) => (
<div>
{
slides.map(slide => (
<>
{
slide.slide.type === "SomeSlide" ? (
<SomeSlide slideData={slide} />
) : slide.slide.type === "SomeOtherSlide" ? (
<SomeOtherSlide slideData={slide} />
) : slide.slide.type === "AndAnotherSlide" ? (
<AndAnotherSlide slideData={slide} />
) : null
}
</>
))
}
</div>
)
This way I would get the specific slide type info, but as there are many different slides (and many more to come) I'd love to go for an more dynamic approach like that:
const SLIDES_MAP: Record<
SlideType["slide"]["type"],
((props: SlideType) => JSX.Element)
> = {
SomeSlide: SomeSlideComponent,
SomeOtherSlide: SomeOtherSlideComponent,
AndAnotherSlide: AndAnotherSlideComponent
};
const Slide = ({ slideData }: ISlideProps) => {
const RenderSlide = SLIDES_MAP[slideData.slide.type];
if (!RenderSlide) return null;
return (
<RenderSlide
slide={slideData.slide}
sys={slideData.sys}
nextSlide={slideData.nextSlide}
/>
);
};
export const Example = ({ slides }: { slides: SlideType[] }) => (
<div>
{
slides.map(slide => (
<Slide slideData={slide} />
))
}
</div>
)
With that a slide itself would look something like that:
const SomeSlideComponent = ({
slide,
nextSlide,
sys,
}: SlideType & { slide: SomeSlideType }) => ( // <====== interesting part
<h1>
{slide.headline}
</h1>
);
export default SomeSlideComponent;
Of course it now complains, that the return type of SomeSlideComponent doesn't match with ((props: SlideType) => JSX.Element) as we manipulated the type to SlideType & { slide: SomeSlideType } to specify it (and SomeSlideType !== SomeSlideType | SomeOtherSlideType | AndAnotherSlideType
Still, to come to the MAIN question, with SlideType & { slide: SomeSlideType } I thought somehow I could define it as a valid "value" as I defined the SlideType earlier with:
type SlideType = {
sys: { id: string };
nextSlide: { id: string };
slide: SomeSlideType | SomeOtherSlideType | AndAnotherSlideType;
};
so SomeSlideType is actually a valid option for slide
anyone has an idea how to tackle this? some TS trick? I tried to use Partial<XYZ> too, but maybe I'm just to unskilled with that 🥲
I really hope I could pin down the issue in an understandable way, otherwise please feel free to request a more specific description on something :)
Thank you and cheers!

Dynamic className manipulation in React

I am havving a problem with dynamic class change in my react component. In a simple slider i want to display only selected image.
My component looks like this:
import React, { useState } from 'react';
import './Slider.scss';
import SliderImage from './SliderImage';
import photo1 from '../../../Images/photo1.jpg';
import photo2 from '../../../Images/photo2.jpg';
import photo3 from '../../../Images/photo3.jpg';
import photo4 from '../../../Images/photo4.jpg';
import photo5 from '../../../Images/photo5.jpg';
function Slider() {
let slideArr = [
{
id: 1,
src: photo1,
text: 'Far far away, behind the word mountains',
alt: 'wedding_photo',
},
{
id: 2,
src: photo2,
text: 'Far far away, behind the word mountains',
alt: 'wedding_photo',
},
{
id: 3,
src: photo3,
text: 'Far far away, behind the word mountains',
alt: 'wedding_photo',
},
{
id: 4,
src: photo4,
text: 'Far far away, behind the word mountains',
alt: 'wedding_photo',
},
{
id: 5,
src: photo5,
text: 'Far far away, behind the word mountains',
alt: 'wedding_photo',
},
];
const [x, setX] = useState(0);
const goLeft = () => {
console.log(x);
x === 0 ? setX(-100 * (slideArr.length - 1)) : setX(x + 100);
};
const goRight = () => {
console.log(x);
x === -100 * (slideArr.length - 1) ? setX(0) : setX(x - 100);
};
return (
<div className='Slider'>
{slideArr.map((item) => {
return (
<div
className={`Slider__Slide + ${item.id - 1 === x ? 'active' : ''}`}
key={item.id}
style={{ transform: `translateX(${x}%)` }}>
<SliderImage
classTxt={'Slider__Slide-txt'}
classImg={'Slider__Slide-img'}
src={item.src}
text={item.text}
alt={item.alt}
/>
</div>
);
})}
<button id='goLeft' onClick={goLeft}>
left
</button>
<button id='goRight' onClick={goRight}>
right
</button>
</div>
);
}
export default Slider;
So i have array with photos, that have ids. Based on Id I am showing an image in SliderImage component. This part works good. Images are changing, but I am unable to dynamicly change classNames
Here i want to change class
className={`Slider__Slide + ${item.id - 1 === x ? 'active' : ''}`}
but onClick is not adding active to new image.
Thanks for any help
You are using string literals in a way as if you were concatenating two strings. Everything inside the back ticks will be as it is if they are not variables.
className={`Slider__Slide + ${item.id - 1 === x ? 'active' : ''}`}
Here you are using the plus sign and it will be classname + active when it is evaluated what you want to do is basically remove the plus sign.
className={`Slider__Slide ${item.id - 1 === x ? 'active' : ''}`}
I would suggest you to use the classnames library for such use cases when you have to do a conditional chaining for classes. The following example demonstrates how it would be let's say our item object looks like the following and x is defined in a way that it would evaluate to true.
const item = {
id: 3
};
const x = 2;
const classes = classNames({ firstClassName: true, bar: item.id - 1 === x });
console.log(classes) // => 'className active'
This is fundamentally a string concatenation question. The JSX expression you have has spaces and a + as text inside a template literal. Either use + with string literals or use a template literal, but not both.
Using string literals:
className={"Slider__Slide " + (item.id - 1 === x ? "active" : "")}
Using a template literal:
className={`Slider__Slide ${item.id - 1 === x ? "active" : ""}`}
I'll suggest two things
Change the meaning of 'x' to be the current id instead of the position
Replace the "+" from string template, since it's resulting into a literal "+" Slider__Slide + ${item.id - 1 === x ? 'active' : ''} => "Slider__Slide + active" or "Slider__Slide + "
const [x, setX] = useState(0);
const goLeft = () => {
console.log(x);
x === 0 ? setX(slideArr.length - 1) : setX(x);
};
const goRight = () => {
console.log(x);
x === (slideArr.length - 1) ? setX(0) : setX(x);
};
on return
<div
className={`Slider__Slide${item.id - 1 === x ? 'active' : ''}`}
key={item.id}
style={{ transform: `translateX(${x * (-100}%)` }}>
<SliderImage
classTxt={'Slider__Slide-txt'}
classImg={'Slider__Slide-img'}
src={item.src}
text={item.text}
alt={item.alt}
/>
</div>

How to convert a JSON style object to a CSS string?

I wanted to set my element's style as such:
this.refs.element.style = {
...this.props.style,
background: 'blue',
};
But apparently you can't use an object to set the ref's style. I have to use a CSS style string with ; separating the prop:values
I'm aware that most people would set style in the render function, but for performance reasons, I can't repeatedly re-render.
A performant answer is to map and join the Object.entries with semicolons:
const style = {
...this.props.style,
background: 'blue',
};
const styleString = (
Object.entries(style).map(([k, v]) => `${k}:${v}`).join(';')
);
It unwraps background:'blue', to background:blue; which works well for CSS
To replace any capital letter with dash lowercase letter
k = k.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
this solution works in IE and handles camelCase keys like backgroundColor
const style = {
width: '1px',
height: '1px',
backgroundColor: 'red',
transform: 'rotateZ(45deg)',
}
const styleToString = (style) => {
return Object.keys(style).reduce((acc, key) => (
acc + key.split(/(?=[A-Z])/).join('-').toLowerCase() + ':' + style[key] + ';'
), '');
};
console.log(styleToString(style));
// output - "width:1px;height:1px;background-color:red;transform:rotateZ(45deg);"
Use https://www.npmjs.com/package/json-to-css. Note it will not add a semicolon to the last property to fix it you can beautify it with https://www.npmjs.com/package/cssbeautify
Example
const cssbeautify = require('cssbeautify')
const Css = require('json-to-css')
const json = {
"h1": {
"font-size": "18vw",
"color": "#f00"
},
".btn": {
"font-size": "18vw",
"color": "#f00"
}
}
const r = Css.of(json)
console.log(r)
const beautified = cssbeautify(r, {
autosemicolon: true
})
console.log(beautified)
Result
console.log src/utils/playground/index.spec.ts:22 // json-to-css
h1{font-size:18vw;color:#f00}
.btn{font-size:18vw;color:#f00}
console.log src/utils/playground/index.spec.ts:29 // cssbeautify
h1 {
font-size: 18vw;
color: #f00;
}
.btn {
font-size: 18vw;
color: #f00;
}
Adding to the great answer of #Artem Bochkarev
I'm adding a snippet to do the opposite conversion as well (string to object) which may come in handy to anyone stumbling here
const style = {
width: '1px',
height: '1px',
backgroundColor: 'red',
transform: 'rotateZ(45deg)',
};
const styleToString = (style) => {
return Object.keys(style).reduce((acc, key) => (
acc + key.split(/(?=[A-Z])/).join('-').toLowerCase() + ':' + style[key] + ';'
), '');
};
const stringToStyle = (style) => {
const styles = {};
style.split(';').forEach((s) => {
const parts = s.split(':', 2);
if (parts.length > 1) {
styles[parts[0].trim().replace(/-([a-z])/ig, (_, l) => l.toUpperCase())] = parts[1].trim();
}
});
return styles;
};
console.log(styleToString(style));
// output - "width:1px;height:1px;background-color:red;transform:rotateZ(45deg);"
console.log(stringToStyle(styleToString(style)));
TL;DR: The problem is that you are overwriting the entire "style" property of the element and losing its prototype and methods. You must add your style object without change the entire property. If you want to apply an object-like style to a DOM element, just do:
Object.assign(this.refs.element.style, {
background: 'blue',
color: 'white',
/** style properties:values goes here */
});
Explanation: The property "style" is an instance of the "CSSStyleDeclaration" interface. If you overwrite the interface it wont be a "CSSStyleDeclaration" anymore. It works when you set a string as value because javascript will pass the string directly to the element, without process anything.
CSSStyleDeclaration Reference Doc:
https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration
If you want to do a test, go to your navigator > inspector > console and paste the code below:
const p1 = document.createElement('p');
p1.style = { color: 'blue' };
const p2 = document.createElement('p');
Object.assign(p2.style, { color: 'blue' });
console.log(p1);
console.log(p2);
The output will be:
<p style=""></p>
<p style="color: blue;"></p>
the css function in #material-ui/system can help you out
check more info here
import React from 'react';
import styled, { ThemeProvider } from 'styled-components';
import NoSsr from '#material-ui/core/NoSsr';
import { createMuiTheme } from '#material-ui/core/styles';
import { compose, spacing, palette, css } from '#material-ui/system';
const Box = styled.div`
${css(
compose(
spacing,
palette,
),
)}
`;
const theme = createMuiTheme();
export default function CssProp() {
return (
<NoSsr>
<ThemeProvider theme={theme}>
<Box color="white" css={{ bgcolor: 'palevioletred', p: 1, textTransform: 'uppercase' }}>
CssProp
</Box>
</ThemeProvider>
</NoSsr>
);
}

Categories

Resources