Year #2, Week #12 💻 🛸
New stuff we learned this week: 🤔
CSS: Object-Fit and Object-Position
- when applying an image as a css background image, we’ve learned that we can control the fit, sizing, and placement of the background image within the containing element:
.my-div-with-img-background {
background-image: url(./cats.jpg);
background-size: cover; /* <-- 👋 */
background-position: center; /* <-- 👋 */
}
- we can achieve the same sort of control over regular
<img />
tags as well, by controlling the dimensions of the image element and describing how we want to fit the image within those dimensions usingobject-fit
andobject-position
:
<style>
img {
width: 200px;
height: 200px;
object-fit: cover; /* <-- 👋 */
object-position: center; /* <-- 👋 */
}
</style>
<img src="./cats.jpg" />
- values for
object-fit
include:contain
(likebackground-size: contain
)cover
(likebackground-size: cover
)fill
(stretch/force image to match dimensions)none
(do nothing, act like a normal image)
object-position
can be set like so:object-position: 50% 50%;
(this is the default)object-position: right top;
object-position: left bottom;
object-position: 100px 30px;
- For more info, and interactive demos, see MDN for object-fit and object-position
HTML: image srcSet
- When it comes to HTML images, we have a thorny problem. Some users of our websites will be viewing from a very small screen, like a phone. While others have massive ultra-high-definition 6k monitors. How big should we make our images then? If we make them beautiful for large monitors, they will load slowly and not look any better for people on phones. If we optimize for phones, they images will load fast for everyone, but look terrible on big screens. What to do!??
- the browser makers have helped us out by extending the HTML spec so that
<img />
tags now supports some new attributes, one of which issrcset
:
<img
srcset="cats-small.jpg 480w, cats-full.jpg 800w"
src="cats-full.jpg"
alt="My rad cat"
/>
- the
srcset
attribute lets us specify more than one image, with information about their actual size. Then, the browser will pick the right source to load based on the width of the screen of the user viewing the page. - because we sometimes do tricky things and display images at different sizes
based on media queries, there’s also another attribute called
sizes
which allows us to explicitly tell the browser what image to use at which screen sizes:
<img
srcset="cats-small.jpg 480w, cats-full.jpg 800w"
sizes="(max-width: 600px) 480px, 800px"
src="cats-full.jpg"
alt="My rad cat"
/>
Javascript: Equality and non-primitive types
- In javascript, primitive values are tested for equality by value:
let x = 3;
let y = 3;
x === y;
// -> true
let str1 = "foo";
let str2 = "foo";
str1 === str2;
// -> true
- However, non-primitives are tested for equality (when using triple-equals) by reference — which basically means “is it actually the same THING in memory”:
let arr1 = [0];
let arr2 = [0];
arr1 === arr2;
// -> false 🧐
let obj1 = {};
let obj2 = {};
obj1 === obj2;
// -> false 🧐
- sort of the flip-side of this is that you can have two variables that REFERENCE THE SAME data structure (like an array or object), and if you mutate one of them, you’re mutating the other at the same time because they are just two variables pointing to the same piece of memory. Consider this snippet of code:
let kids = [`Win`, `Harriet`];
// 🚨 careful! `students` is not a NEW array
// its just another name for the `kids` array
let students = kids;
// 😎 this is `true` because they are THE SAME ARRAY
students === kids;
// -> true
// now we mutate the `students` array
students.push(`Tabitha`);
// the `kids` array got mutated too
console.log(kids);
// -> ['Win', 'Harriet', 'Tabitha'`]
Non-primitive state in React
- Before React calls your render function to re-reconcile the virtual DOM
against the real DOM, if first checks if any of the props have changed using
===
. If no props have changed, it skips re-rendering, for speed’s sake. - This works as expected for primitive values in state, like numbers, booleans, strings, etc. But if you have a piece of state that is an array or object, and you only mutate the state, React won’t see that anything has changed, and won’t re-render, causing a weird bug:
const App: React.FC => () => {
const [pets, setPets] = useState([`Fern`, `Scout`]);
return (
<div onClick={() => {
// 🚨 BAD! `<Pets />` component below won't re-render
// because the `pets` array isn't a NEW array
pets.push(`Dumpy`);
setPets(pets);
}}>
<Pets pets={pets} />
</div>
);
}
- The way to work around this limitation is to use array or object SPREADING to create a new object or array:
const App: React.FC => () => {
const [pets, setPets] = useState([`Fern`, `Scout`]);
return (
<div onClick={() => {
// ✅ WORKS! spreading creates a NEW array
// so react will notice the change and re-render
setPets([ ...pets, `Dumpy` ]);
}}>
<Pets pets={pets} />
</div>
);
}
Useful Links:
Homework Plan (2 weeks)
- 1 day watch Object Fit Video #1
- 1 day watch Object Fit Video #2
- 2 days review all flashcards (in your app)
- 1 day Akron Snowmen assignment
- 2 days touch typing practice
- 8 days Execute Program homework
- OPTIONAL: Tic-Tac-Toe Challenge 💪
Akron Snowmen Assignment
- connect with vscode into your
akron-snowmen
dir - switch your branch to master and pull from upstream
- delete any branches you have hanging around (practice good git hygiene!)
- run
npm install
to make sure you have thehusky
pre-commit hook stuff working from here on out. - create a new branch for this weeks’ work.
- find your name below and complete the assigned work
- when you finish, submit a MR
- for each other student, leave at least one detailed comment requesting a change in the code.
- incorporate at least one of the other student’s suggestions on your MR.
- Important: - once again this week, we’re only working in Storybook.
Starting the next week, we’ll begin putting things together in Next, but not
this week. You should only be using
npm run storybook
andnpm run build-storybook
- when you’re finished, submit a MR, build the Storybook site, and submit both URLs on slack.
Akron Snowman Sub-Assignments
- Tabitha:
- change the
Image
component so that it usesnext/image
<Image />
component, instead of a background image. - use
objectFit
andobjectPosition
props to get it looking right - refer to this documentation if necessary
- images are in the
public/
dir, and should be referenced like this/plow.jpg
, without thepublic/
dir in the path, that’s how Next works. - also, win’s comonent is really the whole block that contains an image and
the text, so
Image
isn’t the best name for it, rename the component and the files. - make sure that the storybook stories still work
- change the
- Win:
- Make a
ContactBlock
component, matching the purple block on the example site. - Much of the content will be just you pulling in and using Tabitha’s
Form
component, but you’ll need to create the rest of the block, and make sure everything has the correct layout and media queries, etc. - remember mobile-first
- create a storybook story fot it
- Make a
- Harriet:
- create a
PinkBlock
component to house Kiah’sPingGrid
comopnent, making it match the example site - use the Button component
- create a storybook story for it
- create a
- Willow:
- create a
Footer
component, to match the one on the example site. - start with mobile first
- create a storybook story for it
- create a
- Kiah:
- create a
HeroBlock
component for the main big first (non-nav) block on the example site - use
next/image
and css positioning wizardry to make the snow plow image be behind the text, don’t use css background images for this one (at least for now) - mobile first
- create a storybook story for it
- incorporate the
Button
component in it.
- create a
Tic Tac Toe Challenge 💪
- go to this url, fork, then clone to vscode
- run
npm install
to get dependencies - create a new branch
- create a working tic-tac-toe game ✅
- hints/helps:
- fire up the dev server with
npm start
and start working - take baby steps, and commit often
- maybe start by just visually making a tic-tac-toe board, with a signal of whose turn it is (X or O), and if someone has won the game.
- take a few minutes to think carefully about what pieces of state you will need
- it’s probably wise to extract a
<Square />
component for each of the nine squares - I put an empty jest test script in the
src/__tests__
dir for you, so if you have something complicated you want to write a function for, consider writing tests, and doing test-first development (making a failing test, then getting it to pass) - remember keep the MINIMUM state possible — don’t track with state what you can DERIVE FROM STATE.
- you might end up wanting a piece of non-primitive state to hold the state of the squares — if you do, remember to spread.
- fire up the dev server with