You will need to open the folder
deferred
from the repo in your project.
Let's hop into our src directory and create a Slider.jsx file.
export default function Slider({ value, deferred, onChange, name, max }) {
return (
<li className="slider">
<label htmlFor={name}>
{name}
{value !== deferred ? " (Updating)" : ""}
</label>
<input
type="range"
id={name}
name={name}
min="0"
max={max}
value={value}
onChange={onChange}
/>
<output htmlFor={name}>
Actual Value: {value} | Deferred Value: {deferred}
</output>
</li>
);
}
- This is what we're going to use gather user input for what values they want applied to their image.
- This is how you tell that something is in an in-between state - deferred and value will be different. You'll see in just a sec.
Let's go create the image it will be applied to: DisplayImage.jsx
import img from "../images/luna.jpg";
const JANK_DELAY = 100;
export default function DisplayImage({ filterStyle }) {
const expensiveRender = () => {
const start = performance.now();
while (performance.now() - start < JANK_DELAY) {}
return null;
};
return (
<>
{expensiveRender()}
<img src={img} alt="Luna" style={{ filter: filterStyle }} />
<p>Last render: {Date.now()}</p>
</>
);
}
- Beyond that it's just going to render of image (in my case it's my lovely asshole dog Luna, feel free to use your own!!)
- Why the date? I want you to be able to see how frequently that image gets re-rendered.
- expensiveRender is masquerading as a component with its return null.
- What it's really doing is just slowing itself down 100ms intentionally so it can simulate jank well.
- Feel free to modify
JANK_DELAY
to simulate speeding up and slowing down your rendering. 100ms was what I needed to notice big jank on my Macbook Air. Yours may be different.
Okay, now head to App.jsx
import { useState } from "react";
import Slider from "./Slider";
import DisplayImage from "./DisplayImage";
export default function App() {
const [blur, setBlur] = useState(0);
const [brightness, setBrightness] = useState(100);
const [contrast, setContrast] = useState(100);
const [saturate, setSaturate] = useState(100);
const [sepia, setSepia] = useState(0);
const filterStyle = `
blur(${blur}px)
brightness(${brightness}%)
contrast(${contrast}%)
saturate(${saturate}%)
sepia(${sepia}%)
`;
return (
<div className="app">
<h1>Deferred Value</h1>
<DisplayImage filterStyle={filterStyle} />
<ul>
<Slider
value={blur}
deferred={blur}
onChange={(e) => setBlur(e.target.value)}
name="Blur"
max="20"
/>
<Slider
value={brightness}
deferred={brightness}
onChange={(e) => setBrightness(e.target.value)}
name="Brightness"
max="200"
/>
<Slider
value={contrast}
deferred={contrast}
onChange={(e) => setContrast(e.target.value)}
name="Contrast"
max="200"
/>
<Slider
value={saturate}
deferred={saturate}
onChange={(e) => setSaturate(e.target.value)}
name="Saturate"
max="200"
/>
<Slider
value={sepia}
deferred={sepia}
onChange={(e) => setSepia(e.target.value)}
name="Sepia"
max="100"
/>
</ul>
</div>
);
}
- Now we can see something rendered. Notice it's super janky.
- You'll notice that DisplayImage is re-rendering 100% of the time. This is because we haven't memoized it and also filterStyle is changing every time a slider is. When props change, a component will always re-render, memoized or not.