33
loading...
This website collects cookies to deliver better user experience
What does a misogynist say when called out on their misogyny? I can't be a misogynist, I have a daughter/wife. What does a misoinstrumentist, like me, say when called out on their misoinstrumenty? I can't be a misointrumentist, I have contributed to Babel.
Also, last time I did npx create-react-app
it created, let me check, ah yes: 41,732 items, totaling 250.7 MB. I've had one SSD and one HDD die of bad sectors in the last two years. Coincidence? I think not!
npm init vite@latest
and follow the prompts to start a react project without typescript. Then:cd what-you-named-your-project
npm install
npm install styled-components
.index.css
and replacing the few rules there with my reset styles as well as the google font @import
. Keeping this as a CSS file is fine: we don't need createGlobalStyle
as we don't need theming or template literal interpolations for these simple CSS rules.Todo
and Container
a bit to make them more flexible.@media
rules with the rest of the styles for the component. I changed the --body-padding
css variable into a bodyPadding
js variable. I don't know why I did that.src
and srcSet
..menu-visible &::before {}
nav.js
to toggle the menu and search input on and off. This is not a React tutorial, but just one quick observation about a subtle change that happens when you port the code to react:[document.documentElement, document.body].forEach((el) =>
menuVisible
? el.classList.add('menu-visible')
: el.classList.remove('menu-visible')
)
[document.documentElement, document.body].forEach((el) =>
el.classList.toggle("menu-visible")
)
menuVisible
state. I'm not pointing this out to say that I'm some genius developer who anticipated this. I only noticed it after I tried to do it with .toggle()
first and it didn't work (the first time useEffect
ran on mount it was toggling the class on, and then, when the button was clicked, setting menuVisible
to true
, it was toggling it off).IntersectionObserver
was my baby!Slider.jsx
.Side note on code organization: there isn't any 😝. I didn't make different folders for components and started with one file per component instead (vite used jsx
extensions and I went with that). Still, even with region folding of code in the editor, Showcase.jsx
got too long even for me, so I turned that into a presentation component, and moved the core styling and functionality into Slider.jsx
.
<StyledSlider ref={slider}>
; the useEffect
callback, which runs only after the first render, gets the first and last slide nodes from the slider ref with standard DOM properties firstChild
and lastChild
.IntersectionObserver
. All it does, when an observed slide "intersects" 50% with the parent slider (threshold: 0.5
), is set the currentSlide
state to that slide's dom node. With that in place, implementing the disabled state and prev/next functions of the buttons becomes trivial.Warning: Rambling ahead. Remember how I said in the intro to this series that I'll try to keep rambling to a minimum? Well, I failed here. Feel free to skip until the next section.
scrollIntoView
dead in its tracks. I set the threshold to 0.5 to make sure prev/next buttons get the disabled attribute as the last slide is halfway in. For whatever reason, though, chrome was fine with me doing btnPrev.disabled = true;
, but it's not fine with React doing it. As you know, all we can do in react is set disabled={currentSlide === firstSlide.current}
and let react update the DOM however and whenever it sees fit. Well, however react is doing it, chrome doesn't like it one bit — if you click next and then previous (IMPORTANT: without scrolling the page at all in between the clicks, otherwise it works fine), as soon as the first slide comes halfway through, and the button is disabled, chrome stops the smooth scrolling.cloneElement
is doing in line 241? That whole acrobatics is just so we don't have to pass an id
prop to each slide in Showcase.jsx:<Slider>
<Slide title="Lamp" img="lamp" alt="lamp photo" link="#"></Slide>
...
</Slider>
function displayProducts(filter) {
products.forEach((p) => p.classList.add("faded-out"));
productsList.addEventListener(
"transitionend",
(e) => {
products.forEach((p) => {
if (filter === "All" || p.dataset.category === filter) {
p.classList.remove("hidden");
setTimeout(() => {
p.classList.remove("faded-out");
}, 0);
} else {
p.classList.add("hidden");
}
});
},
{ once: true }
);
}
transitionend
event, fired on the parent products list element once, controls the fading-in of new products. To account for the fact that you can't animate from display: none
to display: block
, it removes the hidden
class first, and then, a moment later (with the asynchronous setTimeout(() => {}, 0)
, removes the faded-out
class too which transitions the opacity back from 0 to 1.export function ProductsList({ products }) {
const [listFadeOut, setListFadeOut] = useState(false)
useEffect(() => setListFadeOut(true), [products])
return (
<Transition
in={!listFadeOut}
timeout={timeout}
onExited={() => setListFadeOut(false)}
>
{(state) => (
<StyledProductsList
id="products-list"
aria-live="polite"
aria-atomic="true"
aria-relevant="additions removals"
state={state}
>
<TransitionGroup component={null}>
{products.map(({ id, ...props }) => (
<Transition key={id} timeout={timeout}>
{(state) => <Product state={state} {...props} />}
</Transition>
))}
</TransitionGroup>
</StyledProductsList>
)}
</Transition>
)
}
useEffect(() => {}, [products])
), the first <Transition>
component fades out the products list component itself. Not the same effect as fading out all products individually, but close enough. As soon as it fades out, it fades back in (onExited={() => setListFadeOut(false)}
).<TransitionGroup>
delays the appearing/disappearing of individual products using the same timeout
as the fade-out effect of the products list. This is the equivalent of the .hidden
class from the vanilla js version. There's no animation in the styling of the StyledProduct
component, just:display: ${({ state }) => (state === 'entering' ? 'none' : 'flex')};
jsconfig.json
file did... nothing. So I guess you'll have to turn type acquisition off from the settings if the slow autocomplete becomes too much to handle.